前言
最近做了好多搜索题,整理一发比较好的题目,搜索题比赛虽然不怎么考,但是可以提升代码
能力,还是骗分必备(手动滑稽),不多说,步入正题
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;
}