c++ 孤岛营救问题

孤岛营救问题

题目描述

1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为N 行,东西方向被划分为M列,于是整个迷宫被划分为 N×M 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成 P类,打开同一类的门的钥匙相同,不同类门的钥匙不同。
大兵瑞恩被关押在迷宫的东南角,即(N,M)单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入(1,1)单元。另外,麦克从一个单元移动到另一个相邻单元的时间为1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。
试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。

输入

第 1行有 3个整数,分别表示N,M,P的值。[均小于等于10]
第 2 行是1个整数 K[小于等于150],表示迷宫中门和墙的总数。
第 I+2 行(1<=I<=K) ,有 5 个整数,依次为Xi1,Yi1,Xi2,Yi2,Gi: 当Gi>=1时,表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一扇第Gi类的门,当 Gi=0时,表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一堵不可逾越的墙(其中,|Xi1-Xi2|+|Yi1-Yi2|=1,0<=Gi<=P) 。
第K+3行是一个整数S,表示迷宫中存放的钥匙总数。
第K+3+J 行(1<=J<=S),有3个整数, 依次为Xi1,Yi1,Qi: 表示第J 把钥匙存放在(Xi1,Yi1)单元里,并且第J 把钥匙是用来开启第Qi类门的。 (其中 1<=Qi<=P) 。
输入数据中同一行各相邻整数之间用一个空格分隔。

输出

程序运行结束时,将麦克营救到大兵瑞恩的最短时间的值输出。如果问题无解,则输出-1。

样例输入

4 4 9 
9 
1 2 1 3 2 
1 2 2 2 0 
2 1 2 2 0 
2 1 3 1 0 
2 3 3 3 0 
2 4 3 4 1 
3 2 3 3 0 
3 3 4 3 0 
4 3 4 4 0 
2 
2 1 2 
4 2 1 

样例输出

14

提示

存在一个格子有多把钥匙的情况

AC代码

#include <stdio.h>//includes
#include <string.h>//

int n,m,p,k;//定义变量
int d[11][11][11][11];//d[][][[][]存储障碍物的类型
int kn,g[11][11][100],gn[11][11];//kn钥匙的总数 g[][][]是此位置上第几把钥匙是开哪扇门的钥匙
int dx[4]={0,0,1,-1};//dxdy上下左右扩展
int dy[4]={1,-1,0,0};//up
struct point//结构体
{
    char x,y;//x坐标和y坐标
    int step;//步数
    bool key[11];//当前有没有这把钥匙
};
int fun(point u)//计算钥匙的类型  比如一把2类型的钥匙返回2 没有钥匙返回0  有一把2类型和一把1类型的钥匙返回3(2 + 1)
{
    int ans = 0,p = 1;
    for (int i = 1;i <= 10;i ++)
    {
        ans += p * u.key[i];
        p = p * 2;
    }
    return ans;
}
point q[650000],s;//定义数组,变量
bool used[11][11][1024];//up
int f,e;//up
int main()//main()
{
    scanf("%d %d %d",&n,&m,&p);//输入n行,m列,p种门
    scanf("%d",&k);//输入k(障碍物的总数)
    memset(used,0,sizeof(used));//初始化used[][][]和g[][][]为0
    memset(g,0,sizeof(g));//up
    for(int i=1;i<=k;i++)//输入整个孤岛的具体情况
    {
        int a1,b1,a2,b2;//a1b1是第一个xy坐标 a2b2是第二个xy坐标
        scanf("%d%d%d%d",&a1,&b1,&a2,&b2);//输入他们
        scanf("%d",&d[a1][b1][a2][b2]);//输入这个障碍物的类型
        if(d[a1][b1][a2][b2]==0)//如果这是一堵不可逾越的墙
            d[a1][b1][a2][b2]=-1;//那么,就把他标成-1 后面判断条件时要用到
        d[a2][b2][a1][b1]=d[a1][b1][a2][b2];//重复赋值 (比如 坐标(1,2) 和 坐标(1,3) 之间有障碍物 那么 坐标(1,3) 和 坐标(1,2) 之间就一定有一个障碍物)
    }
    scanf("%d",&kn);//钥匙的总数
    memset(gn,0,sizeof(gn));//钥匙的数量
    for(int i=1;i<=kn;i++)//捡起钥匙
    {
        int a1,b1;//钥匙所在的具体位置
        scanf("%d%d",&a1,&b1);//输入他们
        gn[a1][b1]++;//钥匙数量+1
        scanf("%d",&g[a1][b1][gn[a1][b1]]);//输入钥匙
        //如果这个点上有1把钥匙 就输入到g[x][y][1];
        //如果这个点上有2把钥匙 就输入到g[x][y][2];
        //g[x][y][1] = 2就代表这里有开类型2门的钥匙
    }
    s.x=1;s.y=1;s.step=0;//初始化
    memset(s.key,0,sizeof(s.key));//把钥匙的状态初始化
    q[1]=s;//把第一个点入队
    int f=1,e=1;//f出队 e入队
    while(f<=e)//这里开始bfs,不注释了
    {
        point u=q[f++];
        for(int i=0;i<4;i++)
        {
            point v=u;
            v.x=u.x+dx[i];
            v.y=u.y+dy[i];
            v.step=u.step+1;
            memcpy(v.key,u.key,sizeof(u.key));//把u.key 复制到 v.key
            if(v.x<1||v.x>n||v.y<1||v.y>m)continue;//如果此位置不在孤岛上
            if(d[u.x][u.y][v.x][v.y]==-1)continue;//如果这是一堵不可逾越的墙
            if (used[v.x][v.y][fun(v)] == 1) continue;//如果这个位置曾经走过  used[4][2][3] = 1 (两把钥匙的情况)代表(4,2)这个位置有一把2类型和一把1类型的钥匙
            //并且,该位置已走过  used[2][3][2] = 1(一把钥匙的情况)代表这个位置上有一把2类型的钥匙
            if(d[u.x][u.y][v.x][v.y]>0)//如果这里是门
            {
                if(u.key[d[u.x][u.y][v.x][v.y]]==0)continue;//而且没有钥匙
            }
            /*
             (此处少一个门的类型与钥匙的类型对应的逻辑?)
             if (d[][][][] != g[][][])
             continue;
             */
            if(gn[v.x][v.y]>0)//gn:此位置上是否有钥匙, (有几把钥匙)
            {
                for(int j=1;j<=gn[v.x][v.y];j++)
                    v.key[g[v.x][v.y][j]]=1;//g[v.x][v.y][j]:此位置上钥匙的类型
                //v.key[2] = 1:这个点上有开类型2门的钥匙
            }
            if(v.x==n&&v.y==m)
            {
                printf("%d\n",v.step);
                return 0;
            }
            e ++;
            q[e] = v;
            used[v.x][v.y][fun(v)] = 1;
        }
    }
    printf("-1\n");//如果没有退出的话,就是没找到,就输出"-1"
    return 0;//退出
}


posted @ 2019-08-04 21:38  牛大了的牛大  阅读(585)  评论(0编辑  收藏  举报