【暑假集训专题#搜索】

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\n",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里要注意不一定是当前行。思路很简单,只需从第一行第一个开始搜索,如果该位置该列没被标记且为棋盘,那么在这里放上棋子,并标记,因为每行每列不能冲突,所以搜索下一行,比并且棋子数加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\n",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\n",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\n",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\n",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).\n",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\n",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\n",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
*/


你可能感兴趣的:(搜索,专题训练)