经典搜索题
来源:互联网 发布:淘宝网注册企业店铺 编辑:程序博客网 时间:2024/06/10 15:04
前言
最近做了好多搜索题,整理一发比较好的题目,搜索题比赛虽然不怎么考,但是可以提升代码
能力,还是骗分必备(手动滑稽),不多说,步入正题
A Knight’s Journey
原题
POJ - 2488 http://poj.org/problem?id=2488
题意
国际象棋中的马走”日”字格,问能否找到一种方案使得马到达棋盘每一个点,输出字典序最
小的路径
题解
具体思路
暴力dfs,对于每一个点向8个方向扩展路径,并用flag[]数组标记已到达的点,难点在于字典
序最小的路径,我们可以改变搜索的顺序,先搜索字典序小的走法来达到这一目的.
AC代码
#include<cstdio>#include<cstring>using namespace std;const int MAXN=30;bool flag[MAXN][MAXN],b;int all,tmp,cnt;int p,q;int ans1[MAXN],ans2[MAXN];void dfs(int i,int j) { if(flag[i][j]==1) return ; cnt++; ans1[cnt]=i,ans2[cnt]=j; flag[i][j]=1; if(cnt==all) { b=1; return ; } if(i-2>0&&j-1>0&&b==0) { dfs(i-2,j-1); } if(i-2>0&&j+1<p+1&&b==0) { dfs(i-2,j+1); } if(i-1>0&&j-2>0&&b==0) { dfs(i-1,j-2); } if(i-1>0&&j+2<p+1&&b==0) { dfs(i-1,j+2); } if(i+1<q+1&&j-2>0&&b==0) { dfs(i+1,j-2); } if(i+1<q+1&&j+2<p+1&&b==0) { dfs(i+1,j+2); } if(i+2<q+1&&j-1>0&&b==0) { dfs(i+2,j-1); } if(i+2<q+1&&j+1<p+1&&b==0) { dfs(i+2,j+1); } flag[i][j]=0; cnt--;}int main() { int n,ca=0; scanf("%d",&n); while(n--) { memset(flag,0,sizeof(flag)); memset(ans1,0,sizeof(ans1)); memset(ans2,0,sizeof(ans2)); b=0,tmp=0,cnt=0; scanf("%d %d",&p,&q); printf("Scenario #%d:\n",++ca); all=p*q; dfs(1,1); if(b) { for(int i=1; i<=all; i++) { char c=ans1[i]+64; printf("%c%d",c,ans2[i]); } puts(""); puts(""); } else { puts("impossible"); puts(""); } }}
Catch That Cow
原题
POJ - 3278 http://poj.org/problem?id=3278
题意
一个人初始在N位置,要追一头在K位置的不动的奶牛,他有两种走法,第一种向前走一步或
向后走一步,第二种传送到当前位置*2的地方,问最少需要走几步能追上奶牛.
题解
具体思路
bfs,用队列维护人的状态:当前的位置和已走的步数,但只这样子会TLE,我们考虑加一些剪
枝,当人的当前位置已经大于奶牛位置时,向前走和传送到两倍位置都是没有意义的,向后走
一步是当前的最优解,这就是”最优性剪枝”
AC代码
#include<cstdio>#include<cstring>#include<iostream>#include<queue>using namespace std;typedef long long int LL;const int INF=1e9;typedef pair <int,int> P;bool flag[300010];int n,k;int main() { while(~scanf("%d %d",&n,&k)) { int ans=0; memset(flag,0,sizeof(flag)); queue <P> q; q.push(make_pair(n,0)); flag[n]=1; while(!q.empty()) { P tmp=q.front(); flag[tmp.first]=1; q.pop(); if(tmp.first==k) { ans=tmp.second; break; } else { if(tmp.first<k&&flag[2*tmp.first]==0) { q.push(make_pair(2*tmp.first,tmp.second+1)); } if(tmp.first<k&&flag[tmp.first+1]==0) { q.push(make_pair(tmp.first+1,tmp.second+1)); } if(tmp.first>0&&flag[tmp.first-1]==0) { q.push(make_pair(tmp.first-1,tmp.second+1)); } } } printf("%d\n",ans); }}
Prime Path
原题
POJ - 3126 http://poj.org/problem?id=3126
题意
给出一个四位质数,让你每次改变质数的一个数字,且改变后仍为质数,使其最终变为另一个
给定质数,问最小改变次数,若方案不存在,输出Impossible
题解
具体思路
数位bfs,重复到达一个数是没有意义的,用flag[]数组判重,之后对于每一个数,枚举四个位
置从0-9(注意千位不能有0) ,用队列维护是素数的状态,当搜到目标状态时跳出,如果搜到
队列为空还没有出答案,肯定不存在,输出Impossible
AC代码
#include<cstdio>#include<queue>#include<cstring>using namespace std;const int MAXN=30010;typedef pair<int,int> P;bool flag[MAXN];bool is_prime(int num){ for(int i=2;i*i<=num;i++){ if(num%i==0) return false; } return true;}int main(){ int T; int a,b; scanf("%d",&T); while(T--){ bool ans=0; memset(flag,false,sizeof(flag)); scanf("%d %d",&a,&b); queue <P> q; q.push(make_pair(a,0)); flag[a]=true; while(!q.empty()){ P now=q.front(); q.pop(); if(now.first==b){ ans=1; printf("%d\n",now.second); break; } int dig1=now.first/1000; int dig2=(now.first/100)%10; int dig3=(now.first/10)%10; int dig4=now.first%10; for(int i=1;i<=9;i++){ if(flag[i*1000+dig2*100+dig3*10+dig4]==false&&is_prime(i*1000+dig2*100+dig3*10+dig4)==true){ flag[i*1000+dig2*100+dig3*10+dig4]=true; q.push(make_pair(i*1000+dig2*100+dig3*10+dig4,now.second+1)); } } for(int i=0;i<=9;i++){ if(flag[dig1*1000+i*100+dig3*10+dig4]==false&&is_prime(dig1*1000+i*100+dig3*10+dig4)==true){ flag[dig1*1000+i*100+dig3*10+dig4]=true; q.push(make_pair(dig1*1000+i*100+dig3*10+dig4,now.second+1)); } } for(int i=0;i<=9;i++){ if(flag[dig1*1000+dig2*100+i*10+dig4]==false&&is_prime(dig1*1000+dig2*100+i*10+dig4)==true){ flag[dig1*1000+dig2*100+i*10+dig4]=true; q.push(make_pair(dig1*1000+dig2*100+i*10+dig4,now.second+1)); } } for(int i=0;i<=9;i++){ if(flag[dig1*1000+dig2*100+dig3*10+i]==false&&is_prime(dig1*1000+dig2*100+dig3*10+i)==true){ flag[dig1*1000+dig2*100+dig3*10+i]=true; q.push(make_pair(dig1*1000+dig2*100+dig3*10+i,now.second+1)); } } } if(ans==0){ puts("Impossible"); } }}
Pushing Boxes
原题
POJ - 1475 http://poj.org/problem?id=1475
题意
给出一张用字符表示的图,让你求出人把箱子从起始点推向目标点的路径,如果不行,输出
Impossible.(注意”.”号,一个大坑)
题解
具体思路
难点有2个,第一是路径怎么输出?我们用结构体依次维护每种状态的路径,这样写比较方
便,当然似乎可以回溯路径;
第二是限制状态很多,怎样搜索?我们可以把问题缩小,先bfs箱子的路径,在bfs人的路径,这
样问题就变成了这样:箱子每动一步(此时已保证箱子肯定能动),就看作人从当前位置走向
某一位置(推的方向相反的位置),再搜索一遍,因为题目要先保证箱子推动次数最少,所以在
优先级上,先bfs箱子的路径,在bfs人的路径.
AC代码
#include<iostream>#include<cstdio>#include<map>#include<queue>#include<algorithm>#include<cstring>using namespace std;const int MAXN=30;const int INF=1e9;char maze[MAXN][MAXN];char db[4]= {'S','N','E','W'};char dp[4]= {'s','n','e','w'};int dx[4]= {1,-1,0,0};int dy[4]= {0,0,1,-1};int r,c;int ex1,ey1;bool flagp[MAXN][MAXN];bool flagb[MAXN][MAXN];struct state { int bx,by; int px,py; string ans;} ;state start,now;struct node { int x; int y; string ans;};node nd,tmp;bool check(int x,int y) { if(x<0||x>=r||y<0||y>=c) return false; return true;}bool bfs_person(int sx,int sy,int ex,int ey) { memset(flagp,false,sizeof(flagp)); nd.x=sx; nd.y=sy; nd.ans=""; flagp[nd.x][nd.y]=1; flagp[start.bx][start.by]=1; queue<node> qq; qq.push(nd); while (!qq.empty()) { nd=qq.front(); qq.pop(); if (nd.x==ex&&nd.y==ey) return true; for (int i=0; i<=3; i++) { int nx=nd.x+dx[i]; int ny=nd.y+dy[i]; if (check(nx,ny)&&maze[nx][ny]!='#'&&!flagp[nx][ny]) { flagp[nx][ny]=1; tmp.ans=nd.ans+dp[i]; tmp.x=nx; tmp.y=ny; qq.push(tmp); } } } return false;}bool bfs_box() { queue<state> q; q.push(start); while(!q.empty()) { start=q.front(); q.pop(); for(int i=0; i<=3; i++) { int nx=start.bx+dx[i]; int ny=start.by+dy[i]; int tx=start.bx-dx[i]; int ty=start.by-dy[i]; if(check(nx,ny)&&maze[nx][ny]!='#'&&check(tx,ty)&&!flagb[nx][ny]&&maze[tx][ty]!='#') { if(bfs_person(start.px,start.py,tx,ty)){ flagb[nx][ny]=1; now.px=start.bx; now.py=start.by; now.bx=nx; now.by=ny; now.ans=start.ans+nd.ans+db[i]; if(nx==ex1&&ny==ey1){ return true; } q.push(now); } } } } return false;}int main() { int ca=0; while(~scanf("%d %d",&r,&c)) { if(r==0&&c==0) break; memset(flagb,false,sizeof(flagb)); getchar(); for(int i=0; i<r; i++) { for(int j=0; j<c; j++) { scanf("%c",&maze[i][j]); if(maze[i][j]=='B') { start.bx=i; start.by=j; flagb[i][j]=1; } if(maze[i][j]=='S') { start.px=i; start.py=j; } if(maze[i][j]=='T') { ex1=i; ey1=j; } } if(i!=r-1) getchar(); } start.ans=""; printf("Maze #%d\n",++ca); if(bfs_box()){ cout<<now.ans<<endl; } else puts("Impossible."); puts(""); } return 0;}
Dearboy’s Puzzle
原题
POJ - 2308 http://poj.org/problem?id=2308
题意
连连看问题,具体看题面吧,大致就是给出当前局面,判断能不能消掉所有的块
题解
具体思路
我们注意到消方块的不同顺序是会影响结果的,所以先dfs消块的顺序,对于已确定的顺序
在bfs如何去消,但是这样是会TLE的,我们可以加两步剪枝,第一步是一开始判断场上的方
块数量,如果有奇数的直接输出no,第二个是特判第二种样例的情况(在dfs处剪枝)
AC代码
#pragma GCC optimize(2)#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <queue>using namespace std;int times=0;int dx[4]={1,0,0,-1};int dy[4]={0,1,-1,0};int mp[11][11],num[4];int n,m;bool flag;struct node { int x,y; int turn; int type,d;};bool check(int x,int y){ if(x<0||x>=n||y<0||y>=m) return false; return true; }void bfs(int x,int y,int v,int s[25][2],int &cnt) { cnt=0; queue<node>q; node start,now; start.x=x; start.y=y; start.type=-1; start.turn=0; start.d=-1; bool flag[11][11]; memset(flag,false,sizeof(flag)); flag[x][y]=true; q.push(start); while (!q.empty()) { start=q.front(); q.pop(); if (start.type==v) { s[cnt][0]=start.x; s[cnt++][1]=start.y; continue; } for (int i=0; i<4; i++) { now.x=start.x+dx[i]; now.y=start.y+dy[i]; if (check(now.x,now.y)&&!flag[now.x][now.y]) { if (mp[now.x][now.y]!=-1&&mp[now.x][now.y]!=v) continue; if (start.d==i||start.d==-1) now.turn=start.turn; else now.turn=start.turn+1; if (now.turn<=2) { now.type=mp[now.x][now.y]; now.d=i; flag[now.x][now.y]=true; q.push(now); } } } }}void FO(){ freopen("in.txt","r",stdin);}bool judge(){ for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ if(num[mp[i][j]]==2&&num[mp[i+1][j]]==2&&mp[i][j]==mp[i+1][j+1]&&mp[i+1][j]==mp[i][j+1]) return false; } } return true;}void dfs(int cnt) { if (flag) return ; if (cnt==0) { flag=true; return; } if(judge()==false) return ; for (int i=0; i<n; i++) { for (int j=0; j<m; j++) { if (mp[i][j]!=-1) { int s[25][2]; int sum; int v=mp[i][j]; bfs(i,j,v,s,sum); num[v]-=2; mp[i][j]=-1; for (int k=0; k<sum; k++) { int x=s[k][0]; int y=s[k][1]; mp[x][y]=-1; dfs(cnt-2); mp[x][y]=v; } num[v]+=2; mp[i][j]=v; } } }}int main() { int i,j;// FO(); while (~scanf("%d%d",&n,&m)) { if (n==0&&m==0) break; getchar(); flag=false; memset(num,0,sizeof(num)); memset(mp,-1,sizeof(mp)); int all=0; char tmp; for (i=0; i<n; i++) { for (j=0; j<m; j++) { tmp=getchar(); if (tmp=='*') mp[i][j]=-1; else if (tmp=='A') { num[0]++; mp[i][j]=0; all++; } else if (tmp=='B') { num[1]++; mp[i][j]=1; all++; } else if (tmp=='C') { num[2]++; mp[i][j]=2; all++; } else { num[3]++; mp[i][j]=3; all++; } } if(i!=n-1) getchar(); } if (num[0]%2||num[1]%2||num[2]%2||num[3]%2) { printf("no\n"); continue; } dfs(all); if (flag==true) printf("yes\n"); else printf("no\n"); } return 0;}
Eight
原题
POJ - 1077 http://poj.org/problem?id=1077
题意
压轴登场,经典的八数码问题
题解
具体思路
这题写法非常多,A*,IDA*,康托展开都能做,这里讲一种利用康托展开哈希的做法,主要是优
化判重(因为自定义数据类型的map太慢,会TLE)我们把康托展开的每一种状态看作1-9的
一种排列方式,这样我们就得到了每种状态的哈希值,之后根据它判重就行了
AC代码
#include<stdio.h>#include<queue>#include<string.h>#include<iostream>#include<algorithm>using namespace std;const int MAXN=1000000;int fac[]= {1,1,2,6,24,120,720,5040,40320,362880};bool vis[MAXN];int cantor(int s[]) { int sum=0; for(int i=0; i<9; i++) { int num=0; for(int j=i+1; j<9; j++) if(s[j]<s[i])num++; sum+=(num*fac[9-i-1]); } return sum+1;}struct Node { int s[9]; int loc; int status; string path;};string path;int aim=46234;int dx[4]={-1,1,0,0};int dy[4]={0,0,-1,1}; char dans[4]={'u','d','l','r'};Node ncur;bool bfs() { Node cur,next; queue<Node>q; q.push(ncur); while(!q.empty()) { cur=q.front(); q.pop(); if(cur.status==aim) { path=cur.path; return true; } int x=cur.loc/3; int y=cur.loc%3; for(int i=0; i<4; i++) { int nx=x+dx[i]; int ny=y+dy[i]; if(nx<0||nx>2||ny<0||ny>2)continue; next=cur; next.loc=nx*3+ny; next.s[cur.loc]=next.s[next.loc]; next.s[next.loc]=0; next.status=cantor(next.s); if(!vis[next.status]) { vis[next.status]=true; next.path=next.path+dans[i]; if(next.status==aim) { path=next.path; return true; } q.push(next); } } } return false;}int main() { memset(vis,false,sizeof(vis)); char ch; while(cin>>ch) { if(ch=='x') { ncur.s[0]=0; ncur.loc=0; } else ncur.s[0]=ch-'0'; for(int i=1; i<9; i++) { cin>>ch; if(ch=='x') { ncur.s[i]=0; ncur.loc=i; } else ncur.s[i]=ch-'0'; } ncur.status=cantor(ncur.s); if(bfs()) { cout<<path<<endl; } else cout<<"unsolvable"<<endl; } return 0;}
- 经典搜索题
- Sticks 经典搜索题
- 练习:经典搜索题
- 经典搜索题
- pku上一些经典的搜索题
- pku上一些经典的搜索题
- 经典搜索界面范例
- 经典搜索_Poj_1011
- hdu1495之经典搜索
- hdu 1455 搜索经典
- 二分搜索经典题目
- poj1011Sticks 经典搜索
- POJ3279Fliptile【经典搜索】
- POJ 1011 (经典搜索)
- poj1011 _经典搜索
- 经典记忆化搜索
- 人工智能-超越经典搜索
- 搜索的经典题--根据网上资料整理
- Python中的垃圾回收机制详解
- caffe平台搭建可能遇到的bug及解决方法
- dos笔记-bat文件如何关闭回显
- 修改mysql结束符号
- 习题6.3
- 经典搜索题
- 习题6.5作业
- OOM
- B树、B-树、B+树、B*树
- 正则表达式
- 输入增长率,求产值增长一倍需多少年
- Nginx在linux上安装之前的准备工作
- SDNU_ICPC1115(结构体排序)
- 拉格朗日乘子法和KKT条件