练习:经典搜索题

来源:互联网 发布:轻松的工作 知乎 编辑:程序博客网 时间:2024/06/05 19:22

暴力搜索不管是在平面矩阵还是在树中都是骗分的好方法。
接下来带来两道经典搜索题的题解。

推箱子

POJ 1475 推箱子
推箱子大家都玩过吧。要求推箱子的步数最少,满足条件的情况下人走的步数最少,又满足以上条件的情况下spj。推箱子的动作用大写表示,无解则输出Impossible.
先枚举箱子的上下左右,如果有空位,搜索人能不能走到。

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#include<string>#include<math.h>using namespace std;int bx,by,sx,sy,tx,ty,m,n,d[]={-1,1,0,0,0,0,-1,1},vis[25][25];//是否走到过char caozuo[]={'n','s','w','e'},c[25][25];bool biaoji[25][25][4];//标记盒子的位置以及每个方向struct box{int x,y,sx,sy;string answer;}nowbox,prebox;//sx,sy标记盒子在x,y时人的位置struct ren{int x,y;string answer;}nowman,preman;inline char daxie(char c){return (c-32);}//大写bool bfs2(int s,int e,int a,int b,int p,int q)//判断人能不能走到盒子旁{queue<ren> r;if (s<0||s>m||e<0||e>n||c[s][e]=='#') return false;//不存在的nowman.x=p,nowman.y=q,nowman.answer="";memset(vis,0,sizeof vis);vis[a][b]=1,vis[p][q]=1;for (r.push(nowman);!r.empty();)//平凡的bfs  {  nowman=r.front(),r.pop();  if (nowman.x==s&&nowman.y==e) return true;  for (int i=0;i<4;i++)    {    preman.x=nowman.x+d[i];    preman.y=nowman.y+d[i+4];    if (preman.x>0&&preman.x<=m&&preman.y>0&&preman.y<=n&&!vis[preman.x][preman.y]&&c[preman.x][preman.y]!='#')      {      preman.answer=nowman.answer+caozuo[i];//加上走的一步      vis[preman.x][preman.y]=1;      r.push(preman);      }    }  }return false;}bool bfs1()//一样的{queue<box> b;nowbox.x=bx,nowbox.y=by,nowbox.answer="";nowbox.sx=sx,nowbox.sy=sy;for (b.push(nowbox);!b.empty();)  {  nowbox=b.front(),b.pop();  if (nowbox.x==tx&&nowbox.y==ty) return true;  for (int i=0;i<4;i++)    {    prebox.x=nowbox.x+d[i];    prebox.y=nowbox.y+d[i+4];    if (prebox.x>0&&prebox.x<=m&&prebox.y>0&&prebox.y<=n&&!biaoji[prebox.x][prebox.y][i]&&c[prebox.x][prebox.y]!='#')    if (bfs2(prebox.x-2*d[i],prebox.y-2*d[i+4],nowbox.x,nowbox.y,nowbox.sx,nowbox.sy))      {      prebox.sx=nowbox.x;      prebox.sy=nowbox.y;      prebox.answer=nowbox.answer+nowman.answer+daxie(caozuo[i]);      biaoji[prebox.x][prebox.y][i]=1;      b.push(prebox);      }    }  }return false;}int main(){for (int t,cases=0;scanf("%d %d",&m,&n),m,n;puts(""))//做着做着不要忘记还有一个换行  {  memset(biaoji,false,sizeof biaoji);  for (int i=1;i<=m;i++) for (int j=1;j<=n;j++)     {    scanf(" %c",&c[i][j]);    if (c[i][j]=='S') sx=i,sy=j;    if (c[i][j]=='T') tx=i,ty=j;    if (c[i][j]=='B') bx=i,by=j;    }  printf("Maze #%d\n",++cases);  if (bfs1()) cout<<nowbox.answer.c_str()<<endl;  else puts("Impossible.");  }}

八数码

POJ 1077 Eight
八数码也是经典问题。这题是spj,只要输出的路径是正确的都给对。
这里用了A*算法。

#include<cstdio>#include<queue>#include<cstring>#include<algorithm>#include<ctype.h>#include<string>using namespace std;const int boss=4e5;struct node{int c[3][3],hash,f,g,h,x,y;//hash即为状态bool operator <(const node n1) const{return h!=n1.h?h>n1.h:g>n1.g;}bool check(){return x<0||y<0||x>=3||y>=3?0:1;}};int jiecheng[]={1,1,2,6,24,120,720,5040,40320},d[]={1,-1,0,0,0,0,-1,1},vis[boss+10],pre[boss+10],zhongdian=322560;//终点的hash是322560int getstate(node n)//求一个图的状态{int opt[12],k=0;for (int i=0;i<3;i++) for (int j=0;j<3;++j) opt[k++]=n.c[i][j];int sum=0;for (int i=0;i<9;i++)   {  int cnt=0;  for (int j=0;j<i;j++) if (opt[i]<opt[j]) cnt++;  sum+=cnt*jiecheng[i];  }return sum;}int gujia(node n)//估计还有多少步到达{int sum=0;for (int i=0;i<3;i++) for (int j=0;j<3;++j) if (n.c[i][j])  {  int x=(n.c[i][j]-1)/3,y=(n.c[i][j]-1)%3;  sum+=abs(x-i)+abs(y-j);  }return sum;}bool judge(node n)//判断逆序对是否是偶数,不是直接剪枝{int opt[12],k=0;for (int i=0;i<3;i++) for (int j=0;j<3;++j) opt[k++]=n.c[i][j];int sum=0;for (int i=0;i<9;i++) for (int j=i+1;j<9;++j) if (opt[i]&&opt[j]&&opt[i]>opt[j]) sum++;return !(sum&1);}void a_star(node st)//A*算法,具体可以看看别人的博客{priority_queue<node> q;for (q.push(st);!q.empty();)  {  node n=q.top();q.pop();  for (int i=0;i<4;i++)     {    node xia=n;    xia.x+=d[i],xia.y+=d[i+4];    if (!xia.check()) continue;    swap(xia.c[xia.x][xia.y],xia.c[n.x][n.y]);    xia.hash=getstate(xia);    if (vis[xia.hash]==-1)      {      xia.h=gujia(xia);      xia.g++;      xia.f=xia.g+xia.h;      pre[xia.hash]=n.hash;      q.push(xia);      vis[xia.hash]=i;      }    if (xia.hash==zhongdian) return;    }  }}void print()//输出路径{int nextit=zhongdian;char pr[]={'d','u','l','r'};string answer;answer.clear();for (;pre[nextit]!=-1;nextit=pre[nextit]) answer+=pr[vis[nextit]];int len=answer.size();for (int i=len-1;i>=0;i--) putchar(answer[i]);puts("");}int main(){for (char str[100];gets(str)!=NULL;print())  {  node t;  memset(vis,-1,sizeof vis);  memset(pre,-1,sizeof pre);  for (int k=0,i=0;str[k]!='\0';k++)    if (isdigit(str[k])) t.c[i/3][i++%3]=str[k]-'0';    else if (str[k]=='x') t.x=i/3,t.y=i%3,t.c[t.x][t.y]=0,i++;  t.hash=getstate(t);  vis[t.hash]=-2;  t.g=0,t.h=gujia(t);  t.f=t.g+t.h;  if (!judge(t)) {puts("unsolvable");continue;}  if (t.hash==zhongdian) {puts("");continue;}  a_star(t);  }}

谢谢大家的观看。