迷宫问题:给出起点和终点,求解到最小步数或最小转向数
基本思路:bfs+队列优化,搜索在当前位置的状态的转移,是否到达过该状态则用vis标记,则一定能达到搜索结束的状态,复杂度是状态数
对bfs的理解:bfs可以理解为地毯式搜索,当从起点出发时,达到一步能到达的地方,在搜索两步能到达的地方,是一层一层分级的,假如是最小改变方向数的时候,即为改变一次方向能到达的地方,然后在搜索两步能到达的地方,如果状态并不是一步一步增大,就可能用的着优先队列,或者也可以加一个数组,表示新搜索的结果,是否小于当前答案,感觉用bfs来搜索迷宫问题,就像spfa最短路一样,优先确定第一步能达到的地方,再确定能用第一步松驰的结果来松弛的边,依次进行下去
对状态的理解:如hdu胜利大逃亡续中每当你处于某个位置时,你身上存在哪几把钥匙,当到达一扇门的时候,你是否能通过则通过钥匙来判断
1.hdu 1429 胜利大逃亡(续)
算法思路: 第一次想的状态是下x,y,key,lock.但后来发现lock的状态可不要,没必要特意去处理lock,因为x,y,已经确定了是否是锁,以及能不能通过;
状态 vis[x][y][key] 钥匙的处理用状态压缩,另外注意状态是否合法,是否在地图中,是否访问过,是否超过时间
#includeusing namespace std; int n,m,t,sx,sy,ex,ey,vis[21][21][(1<<11)];char mp[21][21]; int x_[4]={1,-1,0,0},y_[4]={0,0,1,-1}; struct node{ int x,y,time,key; }; bool inmp(int x,int y){ if(x>=1&&x<=n&&y>=1&&y<=m&&mp[x][y]!='*') return true; else return false; } int bfs() { queue q; memset(vis, 0, sizeof(vis)); node s,u,v; s.x = sx,s.y = sy,s.key = 0,s.time=0; vis[s.x][s.y][s.key] = 1; q.push(s); while(!q.empty()) { u=q.front();q.pop(); if(u.x==ex&&u.y==ey&&u.time return u.time; for(int i=0;i<4;i++) { v.x=u.x+x_[i],v.y=u.y+y_[i],v.key=u.key; v.time=u.time+1; if(v.time>=t) continue;//时间小于1,退出 if(!inmp(v.x,v.y)) continue;//不在地图中退出 if(vis[v.x][v.y][v.key]) continue; char c=mp[v.x][v.y]; if(c>='a'&&c<='j') { v.key|=(1<<(c-'a')); if(vis[v.x][v.y][v.key]) continue; } if(c>='A'&&c<='J'){ if(!(v.key&(1<<(c-'A')))) continue; } vis[v.x][v.y][v.key]=1; q.push(v); } } return -1; } int main() { while(~scanf("%d%d%d",&n,&m,&t)) { for (int i = 1; i <= n; i ++){ getchar(); for (int j = 1; j <= m; j ++){ scanf("%c",&mp[i][j]); if (mp[i][j] == '@'){sx = i; sy = j;} if (mp[i][j] == '^'){ex = i; ey = j;} } } int ans=bfs(); printf("%d\n",ans); } return 0; }
2.hdu 1254 推箱子
算法思路:状态 箱子的位置,人的位置,以及推箱子的步数,vis[x][y][x_][y_],如果人和箱子重合,则让推箱子,如果不能推动,则该状态不合法!!重中之重!!
#includeusing namespace std; const int INF=0x3f3f3f3f; struct node{ int x,y,x_,y_,step; friend bool operator <(node a,node b){ return a.step>b.step; } }; int t,n,m;int mp[10][10];int num[10][10][10][10]; int sx,sy,sx_,sy_,ex_,ey_,ans=INF; int x_[4]={1,-1,0,0},y_[4]={0,0,-1,1}; bool inmp(int x,int y){ if(x>=1&&x<=n&&y>=1&&y<=m&&mp[x][y]!=1) return true; else return false; } void bfs() { queue q;node s,u,v;ans=INF; memset(num,0x3f,sizeof(num)); //cout< s.x=sx,s.x_=sx_,s.y=sy,s.y_=sy_,s.step=0; q.push(s);num[sx][sy][sx_][sy_]=0; while(!q.empty()) { u=q.front();q.pop(); //printf("%d %d %d %d %d\n",u.x,u.y,u.x_,u.y_,u.step); if(u.x_==ex_&&u.y_==ey_) ans=min(ans,u.step); for(int i=0;i<4;i++) { v=u; v.x+=x_[i],v.y+=y_[i]; if(inmp(v.x,v.y)&&v.step<num[v.x][v.y][v.x_][v.y_]) { if(v.x==v.x_&&v.y==v.y_) { v.x_+=x_[i],v.y_+=y_[i];v.step++; if(!inmp(v.x,v.y)||v.step>=num[v.x][v.y][v.x_][v.y_]) continue; num[v.x][v.y][v.x_][v.y_]=v.step; q.push(v); } else { num[v.x][v.y][v.x_][v.y_]=v.step; q.push(v); } } } } } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&mp[i][j]); if(mp[i][j]==4) sx=i,sy=j; if(mp[i][j]==2) sx_=i,sy_=j; if(mp[i][j]==3) ex_=i,ey_=j; } bfs(); //printf("%d %d %d %d %d %d\n",sx,sy,sx_,sy_,ex_,ey_); if(ans==INF) printf("-1\n"); else printf("%d\n",ans); } return 0; }
3.hdu 1728 逃离迷宫
算法思路:求最小转弯数,状态:位置+转弯次数 vis[x][y] 一旦被走过的位置一定是最小转弯数了
#include#include #include #include #include using namespace std; struct node{ int x,y,t; }; int n,m,sx,sy,ex,ey,k;char mp[101][101];int vis[101][101]; int x_[4]={1,-1,0,0},y_[4]={0,0,1,-1}; bool inmp(node a){ if(a.x>=0&&a.x =0&&a.y '*') return true; else return false; } bool bfs() { node s,u,v;queue q; for(int i=0;i ) for(int j=0;j ) vis[i][j]=0; s.x=sx,s.y=sy,s.t=-1; q.push(s);vis[sx][sy]=1; while(!q.empty()) { u=q.front();q.pop(); if(u.x==ex&&u.y==ey) return true; for(int i=0;i<4;i++) { v=u,v.t=u.t+1;if(v.t>k) break; while(1)//向一侧进行延伸 { v.x+=x_[i],v.y+=y_[i]; if(!inmp(v)) break; if(v.x==ex&&v.y==ey) return true; if(!vis[v.x][v.y]){ q.push(v);vis[v.x][v.y]=1;} } } } return false; } int main() { std::ios::sync_with_stdio(false); cin.tie(0); int t;cin>>t; while(t--) { cin>>n>>m; for(int i=0;i ) for(int j=0;j ) cin>>mp[i][j]; cin>>k>>sy>>sx>>ey>>ex; sx--,sy--,ex--,ey--; if(bfs()) cout<<"yes"<<endl; else cout<<"no"<<endl; } return 0; }
4.hdu 1494 非常可乐
算法思路: 状态 :a,b,s的含量,以及最少倒的次数:vis[s][a] 因总含量一定,初始状态设为s,a,b三者的含量,末状态设为 s=s/2,a=s/2;转移方式有六种,即两者间互相倒满,细节讨论,搜索的力量无穷大啊,是真的暴力!
#includeusing namespace std; struct node{ int s,a,b,t; }; int s,a,b,vis[110][110]; int bfs() { node ss,u,v;queue q; ss.a=0,ss.b=0,ss.s=s,ss.t=0; q.push(ss);vis[ss.a][ss.b]=1; while(!q.empty()) { u=q.front();q.pop(); if(u.a==s/2 && u.s==s/2) return u.t; if(u.s && u.a!=a) //s->a { int c=a-u.a; if(u.s>=c) v.a=a,v.s=u.s-c; else v.a=u.a+u.s,v.s=0; v.b=u.b; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.s && u.b!=b) //s->b { int c=b-u.b; if(u.s>=c) v.b=b,v.s=u.s-c; else v.b=u.b+u.s,v.s=0; v.a=u.a; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.a && u.s!=s) //a->s { int c=s-u.s; if(u.a>=c) v.s=s,v.a=u.a-c; else v.s=u.s+u.a,v.a=0; v.b=u.b; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.a && u.b!=b) //a->b { int c=b-u.b; if(u.a>=c) v.b=b,v.a=u.a-c; else v.b=u.b+u.a,v.a=0; v.s=u.s; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.b && u.a!=a) //b->s { int c=a-u.a; if(u.b>=c) v.a=a,v.b=u.b-c; else v.a=u.a+u.b,v.b=0; v.s=u.s; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } if(u.b && u.s!=s) //b->a { int c=s-u.s; if(u.b>=c) v.s=s,v.b=u.b-c; else v.s=u.s+u.b,v.b=0; v.a=u.a; v.t=u.t+1; if(!vis[v.a][v.b]) { q.push(v); vis[v.a][v.b]=1; } } } return 0; } int main() { while(~scanf("%d%d%d",&s,&a,&b),a+b+s) { memset(vis,0,sizeof(vis)); if(s&1) {printf("NO\n");continue;} if(a<b) swap(a,b); int ans=bfs(); if(ans) printf("%d\n",ans); else printf("NO\n"); } return 0; }
5.hdu 1253 胜利大逃亡
算法思路 :三位迷宫 ,模板即可
#includeusing namespace std; int a_,b_,c_,ti; struct node{ int x,y,z,t; }; int mp[51][51][51]; int x_[6]={0,0,0,0,1,-1},y_[6]={0,0,1,-1,0,0},z_[6]={1,-1,0,0,0,0}; bool inmp(node a){ if(a.x>=1&&a.x<=a_&&a.y>=1&&a.y<=b_&&a.z>=1&&a.z<=c_) return true; else return false; } int bfs() { node s,tmp,nn;queue q; s.x=1,s.y=1,s.z=1,s.t=0; q.push(s);mp[1][1][1]=1; while(!q.empty()) { tmp=q.front();q.pop(); if(tmp.t>ti) continue; if(tmp.x==a_&&tmp.y==b_&&tmp.z==c_) return tmp.t; for(int i=0;i<6;i++) { nn.x=tmp.x+x_[i]; nn.y=tmp.y+y_[i]; nn.z=tmp.z+z_[i]; if(!inmp(nn)) continue; if(mp[nn.x][nn.y][nn.z]) continue; nn.t=tmp.t+1; mp[nn.x][nn.y][nn.z]=1; q.push(nn); } } return -1; } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d%d%d",&a_,&b_,&c_,&ti); for(int i=1;i<=a_;i++) for(int j=1;j<=b_;j++) for(int k=1;k<=c_;k++) scanf("%d",&mp[i][j][k]); int ans=bfs(); printf("%d\n",ans); } return 0; }
6.hdu 1242 Rescue
算法思路:基础迷宫+优先队列,vis[x][y],因为杀死敌人会浪费时间,所以用优先队列保持bfs用时间的分层性
#include#include #include #include #include using namespace std; struct node{ int x,y,t; friend bool operator < (node a,node b){ return a.t>b.t; } }; int sx,sy,ex,ey,n,m; char mp[210][210];bool vis[210][210]; int x_[4]={1,-1,0,0},y_[4]={0,0,1,-1}; bool inmp(node a){ if(a.x>=1&&a.x<=n&&a.y>=1&&a.y<=m&&mp[a.x][a.y]!='#') return true; else return false; } int bfs() { node s,u,v;priority_queue q; memset(vis,0,sizeof(vis)); s.x=sx,s.y=sy,s.t=0;q.push(s);vis[sx][sy]=true; while(!q.empty()) { u=q.top();q.pop(); //cout< if(mp[u.x][u.y]=='r') return u.t; for(int i=0;i<4;i++) { v.x=u.x+x_[i],v.y=u.y+y_[i]; if(!inmp(v)||vis[v.x][v.y]) continue; if(mp[v.x][v.y]=='x') v.t=u.t+2; else v.t=u.t+1; vis[v.x][v.y]=true; q.push(v); } } return 0; } int main() { while(cin>>n>>m) { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { cin>>mp[i][j]; if(mp[i][j]=='a')sx=i,sy=j; } int ans=bfs(); if(ans) cout< else cout<<"Poor ANGEL has to stay in the prison all his life."<<endl; } return 0; }endl;
7.hdu 1181 变形课
算法思路:建图,迷宫模板题,或者计算最短路
#includeusing namespace std; const int INF=1000; const int n=25; int mp[27][27],dis[27],vis[27];char str[100]; void spfa(int s) { for(int i=0;i ) dis[i]=INF,vis[i]=0; queue<int>q; q.push(s);dis[s]=0;vis[s]=1; while(!q.empty()) { int now=q.front();q.pop();vis[now]=0; for(int i=0;i ) { if(dis[i]>dis[now]+mp[now][i]) { dis[i]=dis[now]+mp[now][i]; if(!vis[i]){ vis[i]=1; q.push(i); } } } } return; } void init() { for(int i=0;i<26;i++) for(int j=0;j<26;j++) mp[i][j]=(i==j)?0:INF; } int main() { init(); while(~scanf("%s",&str)) { if(str[0]!='0') { int len=strlen(str); int a=str[0]-'a',b=str[len-1]-'a'; mp[a][b]=1; } else { int s='b'-'a',e='m'-'a'; spfa(s); if(dis[e] "Yes.\n"); else printf("No.\n"); init(); } } return 0; }
8.hdu 1180 诡异的楼梯
算法思路:状态:坐标和时间,vis[x][y],同时由时间的奇偶性确定能否到达楼梯的对面,同时用优先队列进行分层,对题意的理解至关重要
#include#include #include #include #include using namespace std; struct node{ int x,y,t; friend bool operator < (node a,node b){ return a.t>b.t; } }; char mp[21][21];int num[21][21]; int n,m,sx,sy,ex,ey,ans; int x_[4]={1,-1,0,0},y_[4]={0,0,1,-1}; bool inmp(node a){ if(a.x>=1&&a.x<=n&&a.y>=1&&a.y<=m&&mp[a.x][a.y]!='*') return true; else return false; } /*5 5 **..T **.*. ..|.. .*.*. S....*/ void bfs() { node s,u,v;priority_queue q; s.x=sx,s.y=sy,s.t=0; q.push(s);memset(num,0x3f,sizeof(num)); while(!q.empty()) { u=q.top();q.pop(); //cout< for(int i=0;i<4;i++) { v.x=u.x+x_[i],v.y=u.y+y_[i]; if(!inmp(v)) continue; if(mp[v.x][v.y]=='|') { if((u.t%2==0&&i<=1)||(u.t%2&&i>=2)) { v.x+=x_[i],v.y+=y_[i]; v.t=u.t+1; if(num[v.x][v.y]&&v.t<num[v.x][v.y]){ num[v.x][v.y]=v.t; if(v.x==ex&&v.y==ey) {ans=v.t;return;} q.push(v); } } else { v=u,v.t++; if(v.x==ex&&v.y==ey) {ans=v.t;return;} q.push(v); } } else if(mp[v.x][v.y]=='-') { if((u.t%2==0&&i>=2)||(u.t%2&&i<=1)) { v.x+=x_[i],v.y+=y_[i]; v.t=u.t+1; if(num[v.x][v.y]&&v.t<num[v.x][v.y]){ num[v.x][v.y]=v.t; if(v.x==ex&&v.y==ey) {ans=v.t;return;} q.push(v); } } else { v=u,v.t++; if(v.x==ex&&v.y==ey) {ans=v.t;return;} q.push(v); } } else { v.t=u.t+1; if(num[v.x][v.y]&&v.t<num[v.x][v.y]){ num[v.x][v.y]=v.t; if(v.x==ex&&v.y==ey) {ans=v.t;return;} q.push(v); } } } } } int main() { std::ios::sync_with_stdio(false); while(cin>>n>>m) { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { cin>>mp[i][j]; if(mp[i][j]=='S') sx=i,sy=j; if(mp[i][j]=='T') ex=i,ey=j; } bfs(); cout< return 0; }endl; }
9.hdu 1175 连连看
算法思路:最小方向数
#includeusing namespace std; int mp[1010][1010],n,m,q_,ss[1010][1010]; int x_[4]={0,0,-1,1},y_[4]={1,-1,0,0}; struct node{ int x,y,step,dir; }; queue q; bool bfs(int x1,int y1,int x2,int y2) { node s,p,t;while(!q.empty()) q.pop(); s.x=x1,s.y=y1,s.dir=0,s.step=0; q.push(s); while(!q.empty()) { p=q.front();q.pop(); if(p.x==x2&&p.y==y2&&p.step<=2) return true; for(int i=0;i<4;i++) { t.x=p.x+x_[i],t.y=p.y+y_[i]; if(t.x<1||t.x>n||t.y<1||t.y>m) continue; t.dir=i+1,t.step=p.step; if(p.dir&&t.dir!=p.dir) t.step++; if(t.step>2) continue; if(!mp[t.x][t.y]||(t.x==x2&&t.y==y2)) if(ss[t.x][t.y]>=t.step){ ss[t.x][t.y]=t.step; q.push(t); } } } return false; } int main() { while(~scanf("%d%d",&n,&m),n+m) { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]); scanf("%d",&q_); int x1,y1,x2,y2; while(q_--) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); if(mp[x1][y1]==0||mp[x2][y2]==0||mp[x1][y1]!=mp[x2][y2]||(x1==x2&&y1==y2)) {printf("NO\n");continue;} for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) ss[i][j]=100; ss[x1][y1]=0; bool flag=bfs(x1,y1,x2,y2); if(flag) printf("YES\n"); else printf("NO\n"); } } return 0; }
10.hdu 1072 Nightmare
算法思路:状态:位置,和此时的剩余时间 vis[x][y][t] ,bfs+队列即可
#includeusing namespace std; struct node{ int x,y,t,step; }; int x_[4]={1,-1,0,0},y_[4]={0,0,1,-1}; int n,m,t,sx,sy,ex,ey,mp[20][20]; bool used[20][20][7];queue q; int bfs() { node s,p,t;queue q; memset(used,0,sizeof(used)); s.x=sx,s.y=sy,s.step=0,s.t=6; q.push(s);used[sx][sy][6]=true; while(!q.empty()) { p=q.front();q.pop(); if(p.x==ex&&p.y==ey&&p.t>=1) return p.step; for(int i=0;i<4;i++) { t.x=p.x+x_[i],t.y=p.y+y_[i]; t.step=p.step+1,t.t=p.t-1; if(t.x<1||t.x>n||t.y<1||t.y>m) continue; if(!mp[t.x][t.y])continue; if(t.t<1) continue; if(used[t.x][t.y][t.t]) continue; if(mp[t.x][t.y]==4){ t.t=6; if(used[t.x][t.y][t.t]) continue; } used[t.x][t.y][t.t]=true; q.push(t); } } return -1; } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&mp[i][j]); if(mp[i][j]==2) sx=i,sy=j; if(mp[i][j]==3) ex=i,ey=j; } int b=bfs(); printf("%d\n",b); } return 0; }
11.hdu 2612 Find a way
算法思路:跑两次bfs模板,合并取最小值即可
#include#include #include #include #include using namespace std; struct node{ int x,y,step; }; int n,m,sx1,sx2,sy1,sy2,tmp;char mp[201][201];int ans1[201][201],ans2[201][201]; bool inmp(int x,int y){ if(x>=1&&x<=n&&y>=1&&y<=m&&mp[x][y]!='#') return true; else return false; } int vis[201][201]; int x_[4]={1,-1,0,0},y_[4]={0,0,1,-1}; void bfs(int sx,int sy) { if(sx==sx1) tmp=1; else tmp=0; if(tmp)memset(ans1,0x3f,sizeof(ans1)); else memset(ans2,0x3f,sizeof(ans2)); memset(vis,0,sizeof(vis)); queue q;node s,u,v; s.x=sx,s.y=sy,s.step=0; q.push(s);vis[sx][sy]=1; while(!q.empty()) { u=q.front();q.pop(); if(mp[u.x][u.y]=='@') if(tmp) ans1[u.x][u.y]=u.step;else ans2[u.x][u.y]=u.step; for(int i=0;i<4;i++) { v.x=u.x+x_[i],v.y=u.y+y_[i],v.step=u.step+1; if(!inmp(v.x,v.y)||vis[v.x][v.y]) continue; vis[v.x][v.y]=1; q.push(v); } } } int main() { std::ios::sync_with_stdio(false); cin.tie(0); while(cin>>n>>m) { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { cin>>mp[i][j]; if(mp[i][j]=='M')sx1=i,sy1=j; if(mp[i][j]=='Y')sx2=i,sy2=j; } bfs(sx1,sy1); bfs(sx2,sy2); int ans_x=0,ans_y=0,ans=100000000; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(mp[i][j]=='@') ans=min(ans,ans1[i][j]+ans2[i][j]); cout< 11<<endl; } }
12.newcoder逃离迷宫
算法思路:bfs+状态:vis[x][y][2] 0代表无钥匙,1代表有钥匙
#include#include using namespace std; struct node{ int x,y,v,t; }; char mp[510][510];int n,m,s_x,s_y,e_x,e_y; int x_[4]={1,0,-1,0},y_[4]={0,1,0,-1}; bool inmp(int x,int y){return x>=0&&x =0&&y<m;} int vis[510][510][2];//0,1 int bfs(){ queue q; q.push({s_x,s_y,0,0}); memset(vis,0,sizeof(vis)); vis[s_x][s_y][0]=1; while(!q.empty()){ node u=q.front();q.pop(); //cout< if(u.x==e_x&&u.y==e_y&&u.v==1) return u.t; for(int i=0;i<4;i++){ node vv=u;vv.x+=x_[i],vv.y+=y_[i];vv.t++; if(!inmp(vv.x,vv.y)) continue; if(mp[vv.x][vv.y]=='K') vv.v=1; if(vis[vv.x][vv.y][vv.v]) continue; vis[vv.x][vv.y][vv.v]=1; if(mp[vv.x][vv.y]=='#') continue; if(mp[vv.x][vv.y]=='E'&&vv.v==0) continue; q.push({vv.x,vv.y,vv.v,vv.t}); } } return -1; } int main(){ int t;scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); for(int i=0;i %s",&mp[i]); for(int i=0;i" ) for(int j=0;j ){ if(mp[i][j]=='P') s_x=i,s_y=j; if(mp[i][j]=='E') e_x=i,e_y=j; } int ans=bfs(); if(ans==-1) printf("No solution\n"); else printf("%d\n",ans); } return 0; }