胜利大逃亡+HDU 1885 Key Task (bfs迷宫状压,钥匙开门问题)
题意:这两个题都是一类问题,迷宫中一些点是门,必须要有对应的钥匙才能开门,才能走,迷宫中的每个点都是能重复走的,问到达终点的最短步数。
思路:有2个问题:1:如何表示某种钥匙的状态(拿或没拿) 2:一个点如何重复走?常规的是vis数组,不能重复走。
1:这就是这类题的巧妙之处了,状压。钥匙的种类肯定是有上限的,题1的钥匙种类最多10把,用10位二进制表示,0代表没拿,1代表拿了。10位也就是2的10次方(最大也就1024)。开一个结
构体,中有key变量,表示该点的钥匙状态。
2:一个点重复走,是拿了钥匙,然后走重复路开门的情况,那vis数组就要开到3维了,vis[][][这一个维度表示钥匙状态的访问],当拿了一把钥匙,vis是会被重新刷新的,这样就能重复走。
代码细节:1:状压其中涉及到的位运算,很基础,看不懂了多看下就懂了
2:!!!!!这两道题最要注意到的细节来了,第二题MLE了很多次,还是c哥给我讲明白了。第一题和第二题的钥匙符号表示是不一样的,一题钥匙是从'a'往后连续表示,而二题不是,虽然钥匙种类还少,但是有'g'。位运算中直接mp[i][j]-'a'是存在大于10的,这样10位二进制1024就不够,vis[][][1024]还会越界,所以要对钥匙符号预处理下,用map的键值存符号,然后实值还是从0开始存。
胜利大逃亡题解:
点击查看代码
//状态压缩,用一个数字来表示多个变量状态,
#include<bits/stdc++.h>
using namespace std;
int n,m,t,st_x,st_y;
char mp[25][25];
int vis[25][25][1025];
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
struct Nod{
int x,y,step,key;
}temp,p;
int bfs(int sx,int sy){
queue<Nod>q;
q.push({sx,sy,0,0});
while(!q.empty()){
temp=q.front();
q.pop();
for (int i = 0; i < 4; i++) {
p.x = temp.x + dir[i][0], p.y = temp.y + dir[i][1], p.step = temp.step + 1, p.key = temp.key;
if (p.x >= 0 && p.x < n && p.y >= 0 && p.y < m && !vis[p.x][p.y][p.key] && mp[p.x][p.y] != '*') {
if (mp[p.x][p.y] == '^' && p.step<t) return p.step;//终点
else if (mp[p.x][p.y] >= 'a' && mp[p.x][p.y] <= 'z') {//钥匙
int stc = mp[p.x][p.y] - 'a';
if ((p.key & (1 << stc)) == 0) { p.key=p.key | (1 << stc); }//没有这把钥匙
}
else if (mp[p.x][p.y] >= 'A' && mp[p.x][p.y] <= 'Z') {//门
int stc = mp[p.x][p.y] - 'A';
if (!(p.key & (1 << stc))) { continue; }//没钥匙
}
q.push(p);
vis[p.x][p.y][p.key] = 1;
}
}
}
return -1;
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&t)){
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++){
getchar();
for(int j=0;j<m;j++){
scanf("%c",&mp[i][j]);
if(mp[i][j]=='@') st_x=i,st_y=j;
}
}
int re=bfs(st_x,st_y);
printf("%d\n",re);
}
return 0;
}
HDU 1885 Key Task题解:(最佳优化)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
char ch[MAXN][MAXN];
int stp[MAXN][MAXN][20];
int sx, sy; // 起始位置
int hx[4] = {0, 1, 0, -1};
int hy[4] = {1, 0, -1, 0};
struct State {
int x, y, st;
};
map<char,int>mp;
queue<State> q;
int bfs(int n, int m) {
memset(stp, -1, sizeof stp);
State sta, to;
sta.x = sx;
sta.y = sy;
sta.st = 0;
q.push(sta);
stp[sta.x][sta.y][sta.st] = 0;
while (!q.empty()) {
to = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
sta.x = to.x + hx[i];
sta.y = to.y + hy[i];
sta.st = to.st;
if (ch[sta.x][sta.y] == '#') continue;
if (sta.x < 0 || sta.y < 0 || sta.x >= n || sta.y >= m) continue;
if (ch[sta.x][sta.y] >= 'a' && ch[sta.x][sta.y] <= 'y') {//这里相对题解1更优化了,用了 | 运算,0|1=1,1|1=1,0|0=1 (0是没钥匙,1是拿到了钥匙,0|1=1)
sta.st |= (1 << mp[ch[sta.x][sta.y]]); // 拿钥匙
}
if ((ch[sta.x][sta.y] >= 'a' && ch[sta.x][sta.y] <= 'y') ||ch[sta.x][sta.y] == '.' ||(ch[sta.x][sta.y] >= 'A' && ch[sta.x][sta.y] <= 'Y' && (sta.st & (1 << (mp[ch[sta.x][sta.y]]))) != 0))
{
if (stp[sta.x][sta.y][sta.st] == -1) {
stp[sta.x][sta.y][sta.st] = stp[to.x][to.y][to.st] + 1;
q.push(sta);
}
}
if (ch[sta.x][sta.y] == 'X') {
return stp[to.x][to.y][to.st] + 1;
}
}
}
return -1;
}
int main() {
int n, m;
mp['b']=0;
mp['B']=0;
mp['y']=1;
mp['Y']=1;
mp['r']=2;
mp['R']=2;
mp['g']=3;
mp['G']=3;
while (cin>>n>>m) {
if(n==0||m==0)break;
while (!q.empty()) q.pop();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> ch[i][j];
if (ch[i][j] == '*') {
sx = i;
sy = j;
ch[i][j] = '.';
}
}
}
int result = bfs(n, m);
if(result==-1)printf("The poor student is trapped!\n");
else printf("Escape possible in %d steps.\n",result);
}
return 0;
}
浙公网安备 33010602011771号