经典搜索题

前言

最近做了好多搜索题,整理一发比较好的题目,搜索题比赛虽然不怎么考,但是可以提升代码

能力,还是骗分必备(手动滑稽),不多说,步入正题

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值