POJ2251-Dungeon Master
题目大意:
一个三维的迷宫,要从'S'找到去'E'的最短步数
解题思路:
bfs
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int dir[6][3]={{1,0,0},{0,1,0},{0,0,1},{-1,0,0},{0,-1,0},{0,0,-1}};
char a[31][31][31];
int vis[31][31][31]; // 到 i,j,k 花的最少步数
int n,m,h;
typedef struct
{
int y;
int x;
int l;
}node;
queue<node>que;
void bfs()
{
while(que.size())
{
node u=que.front();
que.pop();
for(int i=0; i<6; i++)
{
int fy=u.y+dir[i][0];
int fx=u.x+dir[i][1];
int fl=u.l+dir[i][2];
if(fy<1 || fy>n || fx<1 || fx>m || fl<1 || fl>h || a[fy][fx][fl]=='#' || vis[fy][fx][fl]!=inf)
continue;
vis[fy][fx][fl]=vis[u.y][u.x][u.l]+1; // 步数加一
que.push({fy,fx,fl}); // 入队列
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int Y,X,L;
while(cin>>h>>n>>m && n)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
for(int k=1; k<=h; k++)
vis[i][j][k]=inf; // 初始将vis都置为无穷大
for(int k=1; k<=h; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
{
cin>>a[i][j][k];
if(a[i][j][k]=='S')
{
que.push({i,j,k}); // 起点入队列
vis[i][j][k]=0;
}
else if(a[i][j][k]=='E') // 记录终点位置
X=j,Y=i,L=k;
}
bfs();
if(vis[Y][X][L]==inf) // 如果bfs不能搜索到'E'的位置就输出不能逃离
cout<<"Trapped!"<<endl;
else
cout<<"Escaped in "<<vis[Y][X][L]<<" minute(s)."<<endl;
}
return 0;
}
POJ3278-Catch That Cow
题目大意:
给你农夫和牛在x轴上的位置n,k,农夫每次只能向前走一步、向后走一步或者坐标乘以2,牛不会动,问最少多少步可以抓到牛
解题思路:
bfs即可,注意题目给的数据范围是0到100000
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int vis[100005],n,k;
void bfs()
{
queue<int>que;
que.push(n);
vis[n]=0;
while(que.size())
{
int u=que.front();
que.pop();
if(u-1>=0 && vis[u-1]==-1) // 注意判断是否越界
{
que.push(u-1);
vis[u-1]=vis[u]+1;
}
if(u+1<=100000 && vis[u+1]==-1)
{
que.push(u+1);
vis[u+1]=vis[u]+1;
}
if(u*2<=100000 && vis[u*2]==-1)
{
que.push(u*2);
vis[u*2]=vis[u]+1;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
while(cin>>n>>k)
{
memset(vis,-1,sizeof(vis));
bfs();
cout<<vis[k]<<endl;
}
return 0;
}
POJ3279-Fliptile
题目大意:
经典翻砖问题,1表示黑砖,0表示白砖,要把所有的黑砖翻成白砖,每一次翻转都会令其周围上下左右四个方向的砖也发生翻转(也就是一次最多会翻五块砖),求最少步数以及其操作方法
解题思路:
状态压缩+枚举,枚举第一行的操作方法,最多也就是1<<m种可能的操作方法(我这里的m,n和题目中的互换了顺序,我本人更习惯于用n表示行数,m表示列数),在枚举完第一行后从第二行开始遍历,如果上一行同一列a[i-1][j]的位置是1(黑砖),则该砖a[i][j]必须要进行翻转,最后判断最后一行是否都是白砖即可
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int inf=0x3f3f3f3f;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int n,m;
const int N=16;
int a[N][N];
int ba[N][N];
int res[N][N];
int ans[N][N];
void fun(int y, int x) // 翻转函数
{
a[y][x]^=1;
for(int i=0; i<4; i++)
{
int fy=y+dir[i][0];
int fx=x+dir[i][1];
if(fy<1 || fy>n || fx<1 || fx>m)
continue;
a[fy][fx]^=1;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
while(cin>>n>>m)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
cin>>a[i][j];
int ans_num=inf;
for(int state=0; state<1<<m; state++) // 枚举第一行翻转的状态
{
memcpy(ba,a,sizeof(a)); // 将原数组备份下来
memset(res,0,sizeof(res));
int res_num=0;
for(int i=1; i<=m; i++)
{
if(state>>(m-i) & 1) // 题目要求是字典序,因此需要从左往右枚举
{
res[1][i]++; // 记录在该位置进行操作
res_num++;
fun(1,i);
}
}
for(int i=2; i<=n; i++)
for(int j=1; j<=m; j++)
if(a[i-1][j]) // 是 1 就要翻转
{
res[i][j]++;
res_num++;
fun(i,j);
}
int flag=1;
for(int j=1; j<=m; j++) // 判断最后一行是否满足题意
if(a[n][j])
flag=0;
if(flag && res_num<ans_num) // 如果满足题意且翻转次数更少就更新答案
{
memcpy(ans,res,sizeof(ans));
ans_num=res_num;
}
memcpy(a,ba,sizeof(a)); // 记得还原数组
}
memset(res,0,sizeof(res));
if(ans_num!=inf)
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
cout<<ans[i][j]<<" ";
cout<<endl;
}
}
else
cout<<"IMPOSSIBLE"<<endl;
}
return 0;
}
POJ3126-Prime Path
题目大意:
给你两个数n,m,每一步可以改变一个数位上的数字,但操作完成后该数只能是素数,问最少多少次操作可以使n与m相等
解题思路:
纯bfs
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <queue>
#include <algorithm>
#define ll long long
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
const int inf=0x3f3f3f3f;
int vis[10005],a[4];
int b[10005];
int n,m;
int fun(int num, int pos, int change)
{
int i=0;
while(num)
{
a[i++]=num%10;
num/=10;
}
a[(4-pos)]=change;
return a[0]+a[1]*10+a[2]*100+a[3]*1000;
}
void bfs()
{
queue<int>que;
que.push(n);
vis[n]=0;
while(que.size())
{
int u=que.front();
que.pop();
if(u==m)
break;
for(int i=1; i<=4; i++)
for(int j=0; j<10; j++)
{
if((i==1 && j==0) || (i==4 && j%2==0)) // 题目要求只能是四位数因此第一位不能是0
continue;
int t=fun(u, i, j);
if(vis[t]!=-1 || b[t]==0)
continue;
vis[t]=vis[u]+1;
que.push(t);
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
memset(b,-1,sizeof(b));
b[1]=0;
for(int i=2; i*i<=10000; i++) // 素数打个表
if(b[i])
for(int j=i+i; j<=10000; j+=i)
b[j]=0;
cin>>T;
while(T--)
{
cin>>n>>m;
memset(vis,-1,sizeof(vis));
bfs();
if(vis[m]!=-1)
cout<<vis[m]<<endl;
else
cout<<"Impossible"<<endl;
}
return 0;
}
POJ3087-Shuffle'm Up
题目大意:
给你两幅牌s1、s2,牌底代表输入字符串的第一个字符,每次操作将两副牌交叉合并(s2的牌在合并后的最下面)然后将最上面的一半变为新的s2,剩下的就是新的s1,问最少多少次可以得到目标合并牌堆
解题思路:
简单bfs,注意牌顶牌底不要搞反就行
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <queue>
#include <map>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int n;
map<string,int>hah; // 用hash表代替vis数组
string ss;
typedef struct
{
string s1;
string s2;
int step;
}node;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
string s1,s2;
cin>>T;
for(int k=1; k<=T; k++)
{
cin>>n;
cin>>s1>>s2>>ss;
hah.clear();
queue<node>que;
que.push({s1,s2,0});
while(que.size())
{
node t=que.front();
que.pop();
string c;
for(int i=0; i<n; i++)
{
c+=t.s2[i]; // s2的牌在最下面
c+=t.s1[i];
}
if(!hah[c])
{
hah[c]=t.step+1;
que.push({c.substr(0,n),c.substr(n,n),hah[c]});
}
if(c==ss)
break;
}
cout<<k<<" ";
if(hah[ss])
cout<<hah[ss]<<endl;
else
cout<<-1<<endl;
}
return 0;
}
POJ3414-Pots
题目大意:
有两种容量的杯子,最少多少次可以制造出指定体积的水,每次可以倒掉一个杯子的水、装满一个杯子的水或是将这杯水倒到另一个杯子里去(当然水不能溢出来)
解题思路:
每次搜索执行一个操作,用vector来存储操作字符串,dfs就能解决
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=105;
int vis[N][N];
int a,b,c,ans;
typedef struct
{
int a;
int b;
}node;
vector<string>op;
vector<string>op_ans;
void dfs(int x, int y)
{
if(x==c || y==c)
{
if(ans>vis[x][y]) // 如果操作数更少就更新答案
{
op_ans=op;
ans=vis[x][y];
}
return;
}
if(vis[a][y]>vis[x][y]+1)
{
vis[a][y]=vis[x][y]+1;
op.push_back("FILL(1)");
dfs(a, y); // 给 a 杯加满
op.pop_back(); // 将这次操作回溯掉
}
if(vis[x][b]>vis[x][y]+1)
{
vis[x][b]=vis[x][y]+1;
op.push_back("FILL(2)");
dfs(x, b); // 给 b 杯加满
op.pop_back();
}
if(vis[0][y]>vis[x][y]+1)
{
vis[0][y]=vis[x][y]+1;
op.push_back("DROP(1)");
dfs(0, y); // 倒掉 a
op.pop_back();
}
if(vis[x][0]>vis[x][y]+1)
{
vis[x][0]=vis[x][y]+1;
op.push_back("DROP(2)");
dfs(x, 0); // 倒掉 b
op.pop_back();
}
int xx=min(x+y, a);
int xx_y=x+y-xx;
int yy=min(x+y, b);
int yy_x=x+y-yy;
if(vis[xx][xx_y]>vis[x][y]+1) // 把 b 杯倒到 a 杯里
{
vis[xx][xx_y]=vis[x][y]+1;
op.push_back("POUR(2,1)");
dfs(xx, xx_y);
op.pop_back();
}
if(vis[yy_x][yy]>vis[x][y]+1) // 把 a 杯倒到 b 杯里
{
vis[yy_x][yy]=vis[x][y]+1;
op.push_back("POUR(1,2)");
dfs(yy_x, yy);
op.pop_back();
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
while(cin>>a>>b>>c)
{
memset(vis,inf,sizeof(vis));
ans=inf;
op_ans.clear();
op.clear();
vis[0][0]=0;
dfs(0,0);
if(ans==inf)
cout<<"impossible"<<endl;
else
cout<<ans<<endl;
for(int i=0; i<op_ans.size(); i++)
cout<<op_ans[i]<<endl;
}
return 0;
}
UVA11624-Fire!
题目大意:
很普通的迷宫搜索,但是加了会向四周蔓延的火
解题思路:
由于火会向四周蔓延,因此考虑bfs,需要注意的点是之前已经入队列的人可能会在出队列之前被火给覆盖掉
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1005;
char a[N][N];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int n,m;
typedef struct
{
int y;
int x;
int step; // 步数为-1则表示为火
}node;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
queue<node>que;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
{
cin>>a[i][j];
if(a[i][j]=='F')
que.push({i,j,-1});
else if(a[i][j]=='J')
que.push({i,j,0});
}
int flag=-1;
while(que.size())
{
int y=que.front().y;
int x=que.front().x;
int s=que.front().step;
que.pop();
if(a[y][x]=='F') // 在bfs过程中之前人到过的地方可能在之后被火覆盖了,因此需要重新判定
s=-1;
for(int i=0; i<4; i++)
{
int fy=y+dir[i][0];
int fx=x+dir[i][1];
if(fy<1 || fy>n || fx<1 || fx>m) // 发生越界
{
if(s>=0)
{
cout<<s+1<<endl;
flag=1;
goto jieshu; // 跳出bfs
}
}
else if(s>=0 && a[fy][fx]=='.')
{
a[fy][fx]='J';
que.push({fy,fx,s+1});
}
else if(s==-1 && a[fy][fx]!='#' && a[fy][fx]!='F')
{
a[fy][fx]='F';
que.push({fy,fx,s});
}
}
}
jieshu:;
if(flag==-1)
cout<<"IMPOSSIBLE"<<endl;
}
return 0;
}
马走日
题目大意:
给定n*m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。
解题思路:
基础dfs
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
bool vis[11][11];
int dir[8][2]={{2,1},{2,-1},{-2,1},{-2,-1},{1,2},{-1,2},{1,-2},{-1,-2}}; // 马能走八个方向
int ans,n,m;
void dfs(int x, int y,int num)
{
if(num==n*m)
{
ans++;
return;
}
for(int i=0; i<8; i++)
{
int fy=y+dir[i][0];
int fx=x+dir[i][1];
if(fy>=1 && fy<=m && fx>=1 && fx<=n && !vis[fy][fx])
{
vis[fy][fx]=true;
dfs(fx,fy,num+1);
vis[fy][fx]=false;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T,x,y;
cin>>T;
while(T--)
{
memset(vis,false,sizeof(vis));
cin>>n>>m>>x>>y;
x++;
y++;
vis[y][x]=true;
ans=0;
dfs(x,y,1);
cout<<ans<<endl;
}
return 0;
}
POJ4124-海贼王之伟大航路
题目大意:
给定每个岛之间的距离,求一种路线使得从出生岛到终点岛的距离最短,且要经过途中所有岛屿
解题思路:
最短哈密顿路径,状态压缩+dp可以解决
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <queue>
#include <algorithm>
#define ll long long
#define rd(x) scanf("%d",&x)
using namespace std;
const int inf=0x3f3f3f3f;
int dp[1<<18][20];
int a[17][17];
int main()
{
int n,hh;
while(~rd(n))
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
rd(a[i][j]);
for(int i=0; i<1<<n; i++)
for(int j=1; j<=n; j++)
dp[i][j]=inf;
dp[1][1]=0;
for(int state=2; state<1<<n; state++) // 枚举状态
{
for(int i=2; i<=n; i++) // 枚举目标岛屿
{
if((state>>(i-1)) & 1)
{
for(int now=1; now<=n; now++) // 枚举当前可能在的岛屿
{
if((state>>(now-1)) & 1)
{
int ss=state-(1<<(i-1)); // 除去该岛屿之外的状态
dp[state][i]=min(dp[state][i], dp[ss][now]+a[now][i]);
}
}
}
}
}
cout<<dp[(1<<n)-1][n]<<endl;
}
return 0;
}
迷宫问题
题目大意:
找到从起点到终点的最短路径,需要将路线输出
解题思路:
用一个数组保存每一个点是由哪一个点过来的即可
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int a[6][6];
struct pp
{
int y;
int x;
}pre[6][6];
typedef struct
{
int y;
int x;
}node;
bool vis[6][6];
void fun(int y, int x) // 回溯法输出路线
{
if(x==0 && y==0)
{
cout<<"(0, 0)"<<endl;
return;
}
fun(pre[y][x].y,pre[y][x].x);
cout<<"("<<y<<", "<<x<<")"<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
cin>>a[i][j];
memset(vis,false,sizeof(vis));
queue<node>que;
que.push({0,0}); // 起点进队列
vis[0][0]=true;
while(que.size())
{
int y=que.front().y;
int x=que.front().x;
que.pop();
for(int i=0; i<4; i++)
{
int fx=x+dir[i][0];
int fy=y+dir[i][1];
if(fx>=0 && fx<5 && fy>=0 && fy<5 && a[fy][fx]==0 && !vis[fy][fx]) // 判断下一个点是否合法
{
vis[fy][fx]=true;
que.push({fy,fx});
pre[fy][fx].y=y;
pre[fy][fx].x=x;
}
}
}
fun(4,4);
return 0;
}
鸣人和佐助
题目大意:
在基础的迷宫问题下增加了能量值,每从一个大蛇丸手下处通过需要消耗一个单位能量
解题思路:
dfs,用数组vis[i][j][k]来表示当前移动到a[i][j]位置剩余k能量所用的最少步数
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
const int N=205;
char a[N][N];
int vis[N][N][11]; // 到 i,j 位置还剩 k 能量用的最小步数
int n,m;
void dfs(int y, int x, int eng)
{
int step=vis[y][x][eng];
for(int i=0; i<4; i++)
{
int fy=y+dir[i][0];
int fx=x+dir[i][1];
if(fy<1 || fy>n || fx<1 || fx>m)
continue;
if(a[fy][fx]!='#' && vis[fy][fx][eng]>step+1)
{
vis[fy][fx][eng]=step+1;
dfs(fy, fx, eng);
}
if(a[fy][fx]=='#' && eng>0 && vis[fy][fx][eng-1]>step+1)
{
vis[fy][fx][eng-1]=step+1;
dfs(fy, fx, eng-1);
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int eng,X,Y,xx,yy;
while(cin>>n>>m>>eng)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
for(int k=0; k<=eng; k++)
vis[i][j][k]=inf;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
{
cin>>a[i][j];
if(a[i][j]=='@') // 鸣人位置
xx=j,yy=i;
else if(a[i][j]=='+')
X=j,Y=i;
}
vis[yy][xx][eng]=0; // 起点处所用步数为0
dfs(yy,xx,eng);
int ans=inf;
for(int i=0; i<=eng; i++)
ans=min(ans,vis[Y][X][i]);
cout<<((ans==inf) ? -1 : ans)<<endl;
}
return 0;
}
ACwing1113-红与黑
题目大意:
有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
解题思路:
bfs、dfs模板题
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
const int N=21;
int n,m;
char a[N][N];
bool vis[N][N];
typedef struct
{
int y;
int x;
}node;
queue<node>que;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
while(cin>>m>>n && n)
{
memset(vis,false,sizeof(vis));
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
{
cin>>a[i][j];
if(a[i][j]=='@')
{
que.push({i,j});
vis[i][j]=true;
}
}
int ans=0;
while(que.size())
{
node t=que.front();
que.pop();
ans++;
for(int i=0; i<4; i++)
{
int fy=t.y+dir[i][0];
int fx=t.x+dir[i][1];
if(fy<1 || fy>n || fx<1 || fx>m || a[fy][fx]=='#' || vis[fy][fx])
continue;
vis[fy][fx]=true;
que.push({fy,fx});
}
}
cout<<ans<<endl;
}
return 0;
}
POJ1321-棋盘问题
题目大意:
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
解题思路:
用数组vis来表示某一列是否有棋子,行冲突可以用dfs解决
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int n,num,ans;
char a[10][10];
int vis[10];
void dfs(int flag, int score)
{
if(score==num) // 数目达到要求就退出此次搜索
{
ans++;
return;
}
for(int i=flag+1; i<=n; i++) //从下一行开始遍历
{
for(int j=1; j<=n; j++)
{
if(a[i][j]=='#' && !vis[j])
{
vis[j]=true;
dfs(i, score+1);
vis[j]=false;
}
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
while(cin>>n>>num)
{
if(n==-1)
break;
memset(vis,false,sizeof(vis));
ans=0;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
cin>>a[i][j];
for(int i=1; i<=n; i++) // 注意第一行不一定要摆放棋子
for(int j=1; j<=n; j++)
{
if(a[i][j]=='#')
{
vis[j]=true;
dfs(i, 1);
vis[j]=false;
}
}
cout<<ans<<endl;
}
return 0;
}
POJ4116-拯救行动
题目大意:
普通迷宫问题的基础上增加了守卫,从守卫处通过需要消耗额外一个单位的时间
解题思路:
首先想到bfs,由于每一轮入队列所带来的元素的时间可能不同(打败守卫需要消耗一个单位时间)需采用优先队列解决
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int inf=0x3f3f3f3f;
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
const int N=205;
char a[N][N];
int vis[N][N]; // 到 i,j 位置的最小步数
int n,m;
typedef struct
{
int y;
int x;
int step;
}node;
bool operator<(node a, node b)
{
return a.step>b.step;
}
priority_queue<node>que;
void bfs()
{
while(que.size())
{
node t=que.top();
que.pop();
for(int i=0; i<4; i++)
{
int fy=t.y+dir[i][0];
int fx=t.x+dir[i][1];
int step=t.step;
if(fy<1 || fy>n || fx<1 || fx>m)
continue;
if(a[fy][fx]=='x') // 如果下一步是守卫就将步数加1
step++;
if(a[fy][fx]!='#' && vis[fy][fx]>step+1) // 下一步只要不是墙就更新答案
{
vis[fy][fx]=step+1;
que.push({fy, fx, vis[fy][fx]});
}
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int X,Y,T;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
vis[i][j]=inf;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
{
cin>>a[i][j];
if(a[i][j]=='r')
{
vis[i][j]=0;
que.push({i,j,0});
}
else if(a[i][j]=='a')
X=j,Y=i;
}
bfs();
if(vis[Y][X]==inf)
cout<<"Impossible"<<endl;
else
cout<<vis[Y][X]<<endl;
}
return 0;
}
百练4130-Saving Tang Monk
题目大意:
在前一道题的基础上增加了钥匙,需要按顺序集齐钥匙才能前往终点
解题思路:
由于多了钥匙,则需要在原有的vis数组上新加一维表示当前在a[i][j]位置且拿到k钥匙的最少步数,同时为了收集钥匙可能会走已经走过的路,因此在入队列时要将当前已经打败蛇精的状态也加入进去,防止与蛇精二次战斗,蛇精的状态可以通过状态压缩进行存储
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
const int N=205;
char a[N][N];
int vis[N][N][11]; // 到 i,j 位置且已拿到 k 个钥匙的最小步数
int snake[N][N]; // 记录蛇的编号
int n,m,X,Y,ans;
typedef struct
{
int y;
int x;
int k; // 当前拿到手的钥匙
int s; // 当前已击败蛇的状态,最多5条蛇,也就是 1<<5
int step;
}node;
bool operator<(node a, node b)
{
return a.step>b.step;
}
priority_queue<node>que;
void bfs()
{
while(que.size())
{
node t=que.top();
que.pop();
for(int i=0; i<4; i++)
{
int fy=t.y+dir[i][0];
int fx=t.x+dir[i][1];
int s=t.s;
int step=t.step;
int key=t.k;
if(fy<1 || fy>n || fx<1 || fx>n || a[fy][fx]=='#')
continue;
if(a[fy][fx]=='S') // 是蛇
{
if(!(s & (1<<snake[fy][fx]))) // 当前状态下未击败过该蛇
{
s+=(1<<snake[fy][fx]);
step++; // 多用一个单位时间
}
}
if(a[fy][fx]-'0'==key+1) // 是当前状态下可拿的钥匙(唐僧也被看作钥匙)
{
key++;
}
if(vis[fy][fx][key]>step+1) //判断是否更新
{
vis[fy][fx][key]=step+1;
que.push({fy, fx, key, s, vis[fy][fx][key]});
}
}
}
}
int main()
{
while(cin>>n>>m && n)
{
int o=0;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
for(int k=0; k<=m+1; k++)
vis[i][j][k]=inf;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
{
cin>>a[i][j];
if(a[i][j]=='K')
{
vis[i][j][0]=0;
que.push({i,j,0,0,0});
}
else if(a[i][j]=='S')
snake[i][j]=o++; // 存储蛇的编号
else if(a[i][j]=='T')
{
a[i][j]='1'+m; // 将唐僧转化为最后一把钥匙
Y=i;
X=j;
}
}
bfs();
if(vis[Y][X][m+1]==inf)
cout<<"impossible"<<endl;
else
cout<<vis[Y][X][m+1]<<endl;
}
return 0;
}