• 【暑假集训专题#搜索】


    poj 2386 Lake Counting

    【题意】:

    有一个大小为N×M的园子,雨后积起了水。八连通的积水被觉得是连接在一起的。

    请求出园子里总共同拥有多少水洼?(八连通指的是下图中相对W 的*的部分)

    Sample Input

    10 12
    W........WW.
    .WWW.....WWW
    ....WW...WW.
    .........WW.
    .........W..
    ..W......W..
    .W.W.....WW.
    W.W.W.....W.
    .W.W......W.
    ..W.......W.

    Sample Output

    3
    【思路】:

    从随意的W開始,不停地把邻接的部分用'.'取代。1次DFS后与初始的这个W连接的全部W就都被替换成了'.',因此直到图中不再存在W为止,总共进行DFS的次数就是答案了。8个方向共相应了8种状态转移,每一个格子作为DFS的參数至多被调用一次,所以复杂度为O(8×N×M)=O(N×M)。


    代码:

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int N=105;
    int n,m;
    char mat[N][N];
    int dir8[8][2]= {{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
    
    bool ok(int dx,int dy){
        if(dx>=0&&dx<n&&dy>=0&&dy<m) return true;
        return false;
    }
    
    void dfs(int x,int y){
        mat[x][y]='.';
        for(int i=0; i<8; ++i){
            int dx=x+dir8[i][0];
            int dy=y+dir8[i][1];
            if(ok(dx,dy)&&mat[dx][dy]=='W')dfs(dx,dy);
        }
        return ;
    }
    
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF){
            for(int i=0; i<n; ++i) scanf("%s",mat[i]);
            int res=0;
            for(int i=0; i<n; ++i){
                for(int j=0; j<m; ++j){
                    if(mat[i][j]=='W'){
                        dfs(i,j);
                        res++;
                    }
                }
            }
            printf("%d
    ",res);
        } return 0;
    }
    


    poj 1321 棋盘问题
    【题目链接】:click here~~

    【题意】:

    Description

    在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有差别。要求摆放时随意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的全部可行的摆放方案C。

    Input

    输入含有多组測试数据。

     
    每组数据的第一行是两个正整数。n k,用一个空格隔开,表示了将在一个n*n的矩阵内描写叙述棋盘。以及摆放棋子的数目。 n <= 8 , k <= n 
    当为-1 -1时表示输入结束。

     
    随后的n行描写叙述了棋盘的形状:每行有n个字符,当中 # 表示棋盘区域。 . 表示空白区域(数据保证不出现多余的空白行或者空白列)。

     

    Output

    对于每一组数据。给出一行输出。输出摆放的方案数目C (数据保证C<2^31)。

    Sample Input

    2 1
    #.
    .#
    4 4
    ...#
    ..#.
    .#..
    #...
    -1 -1
    

    Sample Output

    2
    1

    【思路】:

    这道题目类似N皇后问题,与之不同的是每一行不一定有棋盘,所以dfs里要注意不一定是当前行。

    思路非常easy。仅仅需从第一行第一个開始搜索,假设该位置该列没被标记且为棋盘,那么在这里放上棋子。并标记。由于每行每列不能冲突,所以搜索下一行,比而且棋子数加1。每次搜索之前先要推断是否棋子已经用完,假设用完。记录方案数加1,然后直接返回。直到所有搜索所有完毕,此时已得到所有方案数。

    此题还需注意标记数组仅仅标记某一列上是否有棋子。由于每次递归下一行,所以每一行不会有冲突,仅仅需推断这一列上是否有其它棋子。

    还要注意改动标记后递归回来要及时复原。


    代码:

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int N=20;
    int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};
    int num,line,res;
    char mat[N][N];
    bool vis_row[N];
    
    void dfs(int _line,int now_num)
    {
        if(now_num==num){///棋子所有放置
            ++res;
            return;
        }
        for(int i=_line; i<line; ++i){
            for(int j=0; j<line; ++j)
                if(mat[i][j]=='#'&&!vis_row[j]){
                    vis_row[j]=true;
                    dfs(i+1,now_num+1);
                    vis_row[j]=false;
                }
        }
    }
    int main()
    {
        while(~scanf("%d%d",&line,&num)&&line!=-1&&num!=-1){
            memset(vis_row,false,sizeof(vis_row));
            for(int i=0; i<line; i++) scanf("%s",mat[i]);
            res=0;
            dfs(0,0);   ///按行搜索,从0行递增;当前放置的棋子
            printf("%d
    ",res);
        }
        return 0;
    }
    

    POJ 3278  catch  the cow

    【题目链接】:click here~~

    【题目大意】:给你两个数 pre,last。共同拥有三种操作+1,-1,*2,使得pre变为last的最小步数。

    【解题思路】:

    常规方法:放入到一个队列里。每次,push,pop出最先入队的数,然后依据三种可能变化入队,中间设一个标记数组。表示变化到该步已经标记了,防止出现4-5-4的可能情况,然后中间一旦出现到目标数,跳出,输出步骤数

    代码:

    #include <stdio.h>
    #include <queue>
    #include <set>
    #include <stack>
    #include <string.h>
    #include <stdlib.h>
    #include <iostream>
    #include <algorithm>
    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N=2*1e6+10;
    typedef long long LL;
    typedef unsigned long long LLU;
    
    int n,m,pre,tp;
    int last,ans,cnt,res;///大数组一般定义在全局
    int nx[N];           ///当前初始值变化的值
    int ny[N];           ///值为tp的步数
    queue<int >vall;
    
    typedef class
    {
    public :
        int val,step;
    private:
        int s1,s2;
    } pos;
    bool vis[N];
    pos que[N];
    
    int main()
    {
        while(~scanf("%d%d",&pre,&last))
        {
            /*
            memset(vis,false,sizeof(vis));
            bfs();
    
            memset(nx,0,sizeof(nx));
            memset(ny,0,sizeof(ny));
            vall.push(pre);        ///初始值压入栈中,5
            nx[pre]=0;             ///达到当前变化的的值须要的步数,nx[5]=0,
            while(vall.size()!=0)
            {
               /// cout<<"vall.size()=" <<vall.size()<<endl;
               /// cout<<"vall.front()="<<vall.front()<<endl;
                tp=vall.front();     ///当前变化到的值
               /// cout<<"[tp]"<<tp<<endl;
               /// cout<<"ny[tp]="<<ny[tp]<<endl;
               /// cout<<"nx[tp]="<<nx[tp]<<endl;
                vall.pop();
                ny[tp]=1;          ///走到该步值是否訪问。防止5-4-5的情况,ny[5]=1,ny[4]=1,
                if(tp==last) break;///走到目标数。跳出
                if(tp-1>=0&&ny[tp-1]==0)
                {
                    vall.push(tp-1);///4,3
                    nx[tp-1]=nx[tp]+1;///nx[4]=1,nx[3]=2;
                    ny[tp-1]=1;     ///ny[4]=1,ny[3]=1;
                }
                if(tp+1<=1e7&&ny[tp+1]==0)
                {
                    vall.push(tp+1);///6
                    nx[tp+1]=nx[tp]+1;///nx[6]=1
                    ny[tp+1]=1;     ///ny[6]=1
                }
                if(tp*2<=1e7&&ny[tp*2]==0)
                {
                    vall.push(tp*2);///10,8
                    nx[tp*2]=nx[tp]+1;///nx[10]=1,nx[8]=2;
                    ny[tp*2]=1;     ///ny[10]=1;ny[10]=1;
                }
            }
            printf("%d
    ",nx[tp]);///输出步数
        }
        return 0;
    }
    5 17
    5--10--9--18--17
    4
    

    队列+广搜。事实上也是队列

    把三种可能情况也增加到还有一个队列里

    代码:

    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    #define lowbit(a) a&-a
    #define Max(a,b) a>b?a:b
    #define Min(a,b) a>b?b:a
    #define mem(a,b) memset(a,b,sizeof(a))
    int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};
    const int N=2*1e6+10;
    int n,m,pre,last;
    bool vis[N];
    
    typedef long long LL;
    typedef unsigned long long LLU;
    
    struct node{
        int val;
        int step;
    }nod;
    
    void bfs(){
        memset(vis,0,sizeof(vis));
        node top,tmp;
        nod.val=pre;
        nod.step=0;
        queue<node >vall;
        while(!vall.empty()) vall.pop();
        vall.push(nod);
        while(!vall.empty()){
            top=vall.front(),vall.pop();
            if(top.val==last){
                printf("%d
    ",top.step);
                return ;
            }
            if(top.val*2<=N&&vis[top.val*2]==0){
                tmp.val=top.val*2;
                tmp.step=top.step+1;
                vis[top.val*2]=1;
                vall.push(tmp);
            }
            if(top.val-1>=0&&vis[top.val-1]==0){
                tmp.val=top.val-1;
                tmp.step=top.step+1;
                vis[top.val-1]=1;
                vall.push(tmp);
            }
            if(top.val+1<=last&&vis[top.val+1]==0){
                tmp.val=top.val+1;
                tmp.step=top.step+1;
                vis[top.val+1]=1;
                vall.push(tmp);
            }
        }
    }
    int main()
    {
        while(~scanf("%d%d",&pre,&last)){
            bfs();
        }
        return 0;
    } 

    hdu 1241

    【题目链接】click here~~

    【题目大意】'@'代表油田位置,'*'代表地面,八个方向相邻的油田视为一个,求给定地图里油田数目

    【解题思路】八个方向搜索就可以

    和poj2386 类似的

    代码:

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int N=1010;
    int dir8[8][2]= {{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
    char mat[N][N];
    int row,line;
    
    bool ok(int dx,int dy){
        if(dx>=0&&dx<=row&&dy>=0&&dy<=line) return true;
        return false;
    }
    
    void dfs(int x,int y){
        mat[x][y]='*';
        for(int i=0; i<8; ++i){
            int dx=x+dir8[i][0];
            int dy=y+dir8[i][1];
            if(ok(dx,dy)&&mat[dx][dy]=='@'){
                dfs(dx,dy);
            }
        }
    }
    int main()
    {
        while(~scanf("%d%d",&row,&line)&&row&&line){
            for(int i=0; i<row; ++i) scanf("%s",mat[i]);
            int res=0;
            for(int i=0; i<row; ++i){
                for(int j=0; j<line; ++j){
                    if(mat[i][j]=='@'){
                        dfs(i,j);
                        res++;
                    }
                }
            }
            printf("%d
    ",res);
        }
        return 0;
    }
    
    /*
    1 1
    *
    3 5
    *@*@*
    **@**
    *@*@*
    1 8
    @@****@*
    5 5
    ****@
    *@@*@
    *@**@
    @@@*@
    @@**@
    0 0
    */

    poj 2251  Dungeon Master

    【题目链接】:click here~~

    【题意】:

    你被困在一个三维地牢,地牢组成单元是一个 立方体,其能够或能够不被填充的岩石。每次你能够它一分钟移动一个单元,从北,南。东,西,向上或向下。你不能移动对角线和迷宫的四面由固体岩石。是否能逃避。假设能输出最短时间。

    【思路】:事实上就是二维广搜的加强版,无非多加了一维。加个推断就能够了。


    代码:

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    #define rep(i,j,k) for(int i=(int)j;i<(int)k;++i)
    #define per(i,j,k) for(int i=(int)j;i>(int)k;--i)
    #define lowbit(a) a&-a
    #define Max(a,b) a>b?

    a:b #define Min(a,b) a>b?b:a #define mem(a,b) memset(a,b,sizeof(a)) typedef long long LL; typedef unsigned long long LLU; typedef double db; const int N=35; const int inf=0x3f3f3f3f; int L,R,C,n,m,t,ans,res,cnt,tmp; char str[N]; bool vis[N][N][N]; char mat[N][N][N];///map int dir6[6][3]= {{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};///六个方向 int pre_i,pre_j,pre_k; int last_i,last_j,last_k; struct node{ int x,y,z,step; }; bool ok(int dx,int dy,int dz){ if(dx>=0&&dx<L&&dy>=0&&dy<R&&dz>=0&&dz<C) return true; return false; } void getMat(){ for(int l=0; l<L; ++l){ for(int row=0; row<R; ++row){ scanf("%s",mat[l][row]); for(int line=0; line<C; ++line){ /// if the 'S' begin if(mat[l][row][line]=='S'){ pre_i=l,pre_j=row,pre_k=line; } /// if the 'E' end if(mat[l][row][line]=='E'){ last_i=l,last_j=row,last_k=line; } } } } } int bfs(){ node a,b; queue <node>vall; a.x=pre_i; a.y=pre_j; a.z=pre_k; a.step=0; vis[pre_i][pre_j][pre_k]=true; vall.push(a); while(!vall.empty()){ a=vall.front(); vall.pop(); /// come to the situation if(a.x==last_i&&a.y==last_j&&a.z==last_k) return a.step; /// the six diretion for(int i=0; i<6; ++i){ b=a; b.x=a.x+dir6[i][0]; b.y=a.y+dir6[i][1]; b.z=a.z+dir6[i][2]; if(ok(b.x,b.y,b.z)&&!vis[b.x][b.y][b.z]&&mat[b.x][b.y][b.z]!='#'){ vis[b.x][b.y][b.z]=true; b.step=a.step+1; vall.push(b); } } } return 0; } int main() { while(scanf("%d%d%d",&L,&R,&C)&&L&&R&&C){ memset(vis,false,sizeof(vis)); getMat(); int res=bfs(); if(res) printf("Escaped in %d minute(s). ",res); else puts("Trapped!"); } return 0; }

     

    poj 1426:

    【题目链接】:click here~~

    【题目大意】:

    给出一个数n。找出一个数要求是n的倍数。而且这个数的十进制仅仅由1和0组成,明显这种数不止一个(假设,满足条件一定会有m×10也满足,故不止一种),题目要求输出随意一个满足该条件的m
    对于数据1,可知2×5=10,故答案能够得出是10(当然,100,1000...也满足,可是special judge。仅仅用输出一个满足条件的解),其它数据也同理。

    【思路】:
    对于数据1。可知2×5=10,故答案能够得出是10(当然,100。1000...也满足,可是special judge,仅仅用输出一个满足条件的解),其它数据也同理。

    代码:
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    
    int n;
    bool ok;
    void dfs(LL now,int deep)
    {
        if(ok) return ;///find the answer
        if(deep>=19) return;/// deep >20
        if(now>=n&&now%n==0){ /// find the answer
            ok=1;
            printf("%lld
    ",now);
            return ;
        }
        dfs(now*10,deep+1);/// dfs now*10
        dfs(now*10+1,deep+1);///dfs now*10+1
    }
    int main()
    {
        while(cin>>n&&n){
            ok=0;
            dfs(1,0);
        }
        return 0;
    }


    poj 3126 Prime Path
    
    
    
    【题目链接】:click here~~
    【题目大意】:给你两个素数,求从n变化到m,每一步仅仅能改变一位上的一个数字,且每次改变得到的数也是素数,求最小的步数
    【思路】:暴力枚举个十百千的位数。注意各位要求是素数则仅仅枚举奇数
    代码:
    //poj 3126
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    #define rep(i,j,k) for(int i=(int)j;i<(int)k;++i)
    #define per(i,j,k) for(int i=(int)j;i>(int)k;--i)
    #define lowbit(a) a&-a
    #define Max(a,b) a>b?a:b
    #define Min(a,b) a>b?b:a
    #define mem(a,b) memset(a,b,sizeof(a))
    
    typedef long long LL;
    typedef unsigned long long LLU;
    typedef double db;
    const int N=15000;
    const int inf=0x3f3f3f3f;
    int n,m,T;
    
    bool vis[N];
    
    int dir4[4][2]= {{1,0},{0,1},{-1,0},{0,-1}};
    int dir8[8][2]= {{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
    int dir6[6][3]= {{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};///六个方向
    
    inline LL read()
    {
        int c=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}
        return c*f;
    }
    
    bool isPrime(int t)///素数推断
    {
        for(int i=2; i<=sqrt(t); ++i)
            if(t%i==0) return false;
        return true;
    }
    
    struct node
    {
        int val,step;
    };
    
    void bfs()
    {
        mem(vis,false);
        node a,b;
        a.val=n;
        a.step=0;
        queue<node >vall;
        vall.push(a);
        while(!vall.empty()){
            a=vall.front();
            vall.pop();
            if(a.val==m){
                printf("%d
    ",a.step);
                return ;
            }
            int ge=a.val%10;///枚举个位
            int shi=a.val%100/10;///枚举十位
    
            for(int i=1; i<=9; i+=2){///个位
                int y=(a.val/10)*10+i;
                if(y!=a.val&&isPrime(y)&&!vis[y]){
                    b.val=y;
                    b.step=a.step+1;
                    vis[y]=1;
                    vall.push(b);
                }
            }
    
            for(int i=0; i<=9; i++){///十位
                int y=(a.val/100)*100+i*10+ge;
                if(y!=a.val&&isPrime(y)&&!vis[y]){
                    b.val=y;
                    b.step=a.step+1;
                    vis[y]=1;
                    vall.push(b);
                }
            }
    
            for(int i=0; i<=9; i++){///百位
                int y=(a.val/1000)*1000+i*100+ge+shi*10;
                if(y!=a.val&&isPrime(y)&&!vis[y]){
                    b.val=y;
                    b.step=a.step+1;
                    vis[y]=1;
                    vall.push(b);
                }
            }
    
            for(int i=1; i<=9; i++){///千位
                int y=a.val%1000+i*1000;
                if(y!=a.val&&isPrime(y)&&!vis[y]){
                    b.val=y;
                    b.step=a.step+1;
                    vis[y]=1;
                    vall.push(b);
                }
            }
        }
    
        puts("Impossible");
        return ;
    }
    int main()
    {
        T=read();
        while(T--){
            n=read(),m=read();
            if(n==m) puts("0");
            else bfs();
        }
        return 0;
    }
    
    /*
    Sample Input
    3
    1033 8179
    1373 8017
    1033 1033
    Sample Output
    6
    7
    0
    */


  • 相关阅读:
    TCP 连接状态
    可视化垃圾回收算法
    flume-ng+Kafka+Storm+HDFS 实时系统搭建
    WeX5 IDE 手机移动开发+JAVA +大数据
    云计算高级运维工程师
    CentOS 5.8 上安装 systemtap-2.6
    SYSTEMTAP -ORACLE
    Apple激活日期查询
    Div 浮动到另一个div之上
    Python模块常用的几种安装方式
  • 原文地址:https://www.cnblogs.com/yfceshi/p/6906770.html
Copyright © 2020-2023  润新知