拯救大兵瑞恩(P4011孤岛营救问题)(bfs+状态压缩)
作者:互联网
题目描述
1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为 N 行,东西方向被划分为 MM列,于是整个迷宫被划分为 N×M 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分成P类,打开同一类的门的钥匙相同,不同类门的钥匙不同。大兵瑞恩被关押在迷宫的东南角,即 (N,M)单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入(1,1) 单元。另外,麦克从一个单元移动到另一个相邻单元的时间为 1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。
试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。
输入格式
第一行有三个整数,分别表示 n,m,p 的值。第二行是一个整数k,表示迷宫中门和墙的总数。
第 i+2 行 (1≤i≤k),有 5 个整数,依次为 xi1,yi1,xi2,yi2,gi:当 gi≥1 时,表示 (xi1,yi1)单元与 (xi2,yi2) 单元之间有一扇第 gi 类的门,当 gi=0 时, 表示 (xi1,yi1) 单元与 (xi2,yi2) 单元之间有一堵不可逾越的墙。
第 k+3 行是一个整数 s,表示迷宫中存放的钥匙总数。
第 k+3+j 行 (1≤j≤s) ,有 3 个整数,依次为 xi1,yi1,qi ,表示第 j 把钥匙存放在 (xi1,yi1) 单元里,并且第 j 把钥匙是用来开启第 qi 类门。
输入数据中同一行各相邻整数之间用一个空格分隔。
|xi1−xi2|+|yi1−yi2|=1,0≤gi≤p|xi1−xi2|+|yi1−yi2|=1,0≤ gi≤p
1≤qi≤p
n,m,p≤10,k<150
题解:
这题目之前搜索专题的时候,不会写,看了下,好像要用网络流,信奥一本通上的解析又没有看懂。然后今天上了洛谷看了下题解,发现好像没有我想的那么复杂,它主要是判重上,和能不能走的问题上有点特殊,判重的话,我开了一个 vector< int > vis [ 16 ] [ 16 ],来标记。至于钥匙的问题,就要用到状态压缩的知识了,可以把每一把钥匙看作成一位二进制数,详细地我推荐洛谷上的这篇知识介绍,我也是看这个的。https://86971.blog.luogu.org/solution-p4011
我感觉他/她写得很好
然后就是典型bfs,四个方向走一遍,先判断位置合不合法,如果合法然后判断从目前这个位置到另一个位置是否有墙,或者是否有门如果有墙,则走不通。如果有门,没有钥匙也走不通,其他情况都可以走过去。
我是用mp[16][16][16][16],一个四维数组来保存地图表示(x1,y1)->(x2,y2),刚开初始化为-1,表示都可以走通,然后根据输入改比如有墙是0。
(1 << mp[x1][y1][x2][y2]) 与 当前的key做与操作。假如结果为零说明,他没有这一把钥匙,门打不开。反之则行。
注意点
- 一个位置可能有多把钥匙,钥匙可以重复使用,初始位置可能也会有钥匙
- 注意数据范围
#include<bits/stdc++.h>
using namespace std;
int N,M,P,K,S;
int mp[16][16][16][16];
vector<int> key[16][16];//一个地方可能会有多把钥匙
int dir[4][2]={-1,0,0,1,1,0,0,-1};
vector<int> vis[16][16];//用这个数组来保存状态
struct node{
int x,y;//当前的坐标,和钥匙数//二进制表示钥匙
int step,k;
}start;
inline bool Catch(node &a)
{
if(a.x==N&&a.y==M) return true;
return false;
}
bool check(int x,int y,node a)
{
if(x<1||x>N||y<1||y>M) return false;
int t=mp[a.x][a.y][x][y];
if(t==-1) return true;
else if(t==0) return false;
else{
if(((1<<t)&a.k)>0) return true; //加括号不然会出错
else return false;
}
}
int bfs()
{
start.x=1,start.y=1,start.step=0;
for(int i=0;i<key[1][1].size();++i)
start.k|=1<<key[1][1][i];
vis[1][1].push_back(start.k);
queue<node>Q;
Q.push(start);
while(!Q.empty())
{
start=Q.front();Q.pop();
if(Catch(start)) return start.step;
for(int i=0;i<4;++i)
{
node r=start;
int x=r.x+dir[i][0],y=r.y+dir[i][1];
if(check(x,y,r)==false) continue;//此路不通
r.x=x,r.y=y,r.step++;
for(int j=0;j<key[x][y].size();++j)
r.k|=(1<<key[x][y][j]); //加括号,不然出错了
int tag=0;
for(int j=0;j<vis[r.x][r.y].size();++j)
if(vis[r.x][r.y][j]==r.k) {tag=1;break;} //已经访问过了
if(tag)continue;
if(Catch(r)) return r.step;
vis[r.x][r.y].push_back(r.k);
Q.push(r);
}
}
return -1;
}
int main()
{
//int N,M,P,K,S;
memset(mp,-1,sizeof(mp)); //-1表示周围是连通的,可以走
cin>>N>>M>>P;
cin>>K;
int x1,y1,x2,y2,g,q;
for(int i=1;i<=K;++i)
{
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&g);
mp[x1][y1][x2][y2]=g; //双向通道
mp[x2][y2][x1][y1]=g;
}
cin>>S;
for(int j=1;j<=S;++j)
{
scanf("%d%d%d",&x1,&y1,&q);
key[x1][y1].push_back(q);
}
cout<<bfs();
return(0);
}
标签:return,16,int,bfs,瑞恩,钥匙,P4011,start,单元 来源: https://blog.csdn.net/NoahBBQ/article/details/117428115