深搜(DFS)模板


当N较小时考虑搜索。

 

DFS大致模板

    //会有相对应的方向变量  例如: dx[4].dy[4];
void DFS(int x,int y)
{
     if(满足所需要的条件)   {  相应的操作;return;}
    else{
            for(int i= ; ;) //如果是方向的话,会枚举方向
            {
                  枚举加方向的新坐标;
                  if(界限 :例如:不能出到地图外,有障碍,已经访问过) continue;
                   设置已经访问新坐标;
                    DFS(新坐标); 
                   恢复到未被访问;
            }

       }
}  

int main()
{
    //  需注意要将起点设置成已访问。
}  

例题:

P1605  迷宫:

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 int n,m,t;
 5 int StrX,StrY;
 6 int EndX,EndY;
 7 int HndX,HndY;
 8 int dx[4]= {1,-1,0,0};
 9 int dy[4]= {0,0,-1,1};
10 int vis[30][30];
11 int ans;
12 void dfs( int x, int y) //深搜
13 {
14     if(x==EndX &&y==EndY) //一定要把坐标打对!!!!!
15     {
16         ans++;
17         return ;
18     }
19     else
20     {
21         for(int i=0; i<4; i++)
22         {
23             int  NewX=x+dx[i],NewY=y+dy[i];
24             //在这道题中,没有0,0结点,所以NewX<1 NewY<1输出才正确
25             if(NewX>n || NewY>m ||NewX<1 ||NewY<1 || vis[NewX][NewY]==1 ||vis[NewX][NewY]==2)  continue;
26             else
27             {
28                 vis[NewX][NewY]=1;
29                 dfs(NewX,NewY);
30                 vis[NewX][NewY]=0;
31             }
32         }
33     }
34 
35 }
36 int main()
37 {
38     scanf("%d%d%d",&n,&m,&t);
39     scanf("%d%d%d%d",&StrX,&StrY,&EndX,&EndY);
40     while(t--)
41     {
42         scanf("%d %d",&HndX,&HndY);
43         vis[HndX][HndY]=2;  //将障碍设置成2,
44     }
45     vis[StrX][StrY]=1;//将起点1设置成已经访问过的状态!!!!切记 
46     dfs(StrX,StrY);
47     printf("%d",ans);
48 
49     return 0;
50 }

 ------------------------------------------------------------------------------------------------------------------------------------------------------

P1149:火柴棒等式

在这道题中,运用了回溯的思想:

过程是:先将所有的数所需要的火柴数遍历计算出来。然后在Search函数中:根据n-number[i]>=0判断是否还能减去当前火柴数。

如果可以,就将这个数的放入b[]中,载判断是否已经A,B,C,三个数都已经给你找齐,如果找齐的话,再判断是否相等,并且n是否恰好用完。

如果l还不等于3的话,在继续寻找下一个数。

最后保存其初始状态。(比较重要)

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 //使用回溯的方法
 7 int n;
 8 int ans;
 9 int b[4];//存找到的三个数
10 int number[1112]= {6,2,5,5,4,5,6,3,7,6};
11 void Search(int l)
12 {
13     for(int i=0; i<1112; i++)
14     {
15 
16         if(n-number[i]>=0)
17         {
18             b[l]=i;
19             n=n-number[i];
20             if(l==3) //如果符合火柴数的三个数都求出来了,然后判断
21                 {if(b[1]+b[2]==b[3] && n==0) ans++;}
22             else Search(l+1);
23             n=n+number[i];  //不管l是不是等于3,都要保存之前的状态
24           //在这里要特别注意,当判断是否l==3,如果=3,判断
25           //如果!=3,再继续下一个数的搜寻。
26         }
27     }
28 }
29 int main()
30 {
31     scanf("%d",&n);
32     n=n-4;
33     for(int i=10; i<1112; i++) //注意这里是从10开始,不是从0开始。
34     {
35         number[i]=number[i/10]+number[i%10]; //在这里也需要注意,在后面三位数中,分割成一个两位数,和一位数,因为两位数,一位数已经算过了,所以就可以直接加起来
36     }
37     Search(1);
38     printf("%d",ans);
39 }

 ----------------------------------------------------------------------------

P1596 Lake Counting

:这道题是深搜模板题。判断连通图有多少。

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 int n,m,ans;
 5 const int MAXN=105;
 6 char map[MAXN][MAXN];
 7 
 8 void dfs(int x,int y)
 9 {
10     for(int i=-1; i<=1; i++)
11     //在这里一共需要8个方向,循环下来加原点一共9个方向
12     {
13         for(int j=-1; j<=1; j++)
14         {
15             int NewX=x+i,NewY=y+j;
16             if(NewX>=n ||NewY>=m ||NewX<0 || NewY<0 || map[NewX][NewY]=='.')
17                 continue; //判断新的坐标的错,不要打错!!
18             map[NewX][NewY]='.';
19             dfs(NewX,NewY);
20         }
21     }
22     return;
23 }
24 int main()
25 {
26     scanf("%d%d",&n,&m);
27     for(int i=0; i<n; i++)
28         scanf("%s",map[i]);
29     for(int i=0; i<n; i++)
30     {
31         for(int j=0; j<m; j++)
32         {
33             if(map[i][j]=='W')
34             {
35                 map[i][j]='.';
36                 ans++;
37                 dfs(i,j);
38             }
39         }
40     }
41     printf("%d",ans);
42     return 0;
43 }

我出现的问题: 

1.在判断界限时手误打错NewX NewY 和x,y;

2. 因为我读入的时候是从下标0开始的,所以在判断界限时应该是NewX>=n 时就已经越界了;

3.之前一直写成4个方向。在这里是8个方向。

------------------------------------------------------------------------------------------------------------------------------

P1219八皇后

采用DFS

#include <iostream>
#include <cstdio>
using namespace std;
int n,a[14],l[27],r[27],ans[14],js=0; //标记是否相应位置上有皇后
void print()
{
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    cout<<endl;
}
void dfs(int i)
{
  if(i>n)
  {
      js++;
      if(js<=3) print();
  }
    for(int j=1;j<=n;j++)
    {
        if(a[j]==0 && l[i+j-1]==0 &&r[i-j+n]==0) 
/*判断界限,当前位置,左对角线,右对角线的*/
        {
            a[j]=1;
            l[i+j-1]=1;
            r[i-j+n]=1;
            ans[i]=j;
            dfs(i+1); 
//恢复初始时的状态 a[j]=0; l[i+j-1]=0; r[i-j+n]=0; } } } int main() { scanf("%d",&n); dfs(1); printf("%d",js); }

对于这道题有几个重要的点:

1.数组大小,

2.判断左对角线和右对角线的公式

3.将答案存储在ans[]数组中

4.输出答案;

------------------------------------------------------------

P1086 花生采摘,采用贪心的方法。

这道题是将所有的可采的花生排序,然后一直找可采的最大的花生。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 using namespace std;
 5 const int MAXN=20;
 6 int map[MAXN][MAXN];
 7 int k,t,m,n,ans,u;
 8 struct node
 9 {
10     int x;
11     int y;
12     int time;
13     int num;
14 }Edge[400];
15 bool cmp(node a,node b)
16 {
17     return a.num>b.num;
18 }
19 void dfs()
20 {
21     for(int i=0; i<k; i++)
22     {
23         u=Edge[i].x+1; //深度
24         if(i==0) Edge[i].time=Edge[i].x+1+1; //第一步的时间等于第一个点的深度+1;因为下标是从0开始,所以要再加1;
25         else Edge[i].time=Edge[i-1].time+abs(Edge[i].x-Edge[i-1].x)
26             +abs(Edge[i].y-Edge[i-1].y)+1; //如果不是第一步的,要算出两个点之间的移动时间,以及采摘时间,以及之前的总时间
27         if(Edge[i].time+u<=t)  {ans+=Edge[i].num;}   //如果采摘花生的过程中花费的时间(包括采摘,走路)加回到原点的时间=约定的时间,则加入花生数。
28     }
29 }
30 int main()
31 {
32     scanf("%d%d%d",&m,&n,&t);
33     for(int i=0; i<m; i++)
34     {
35         for(int j=0; j<n; j++)
36         {
37             scanf("%d",&map[i][j]);
38             if(map[i][j]!=0)
39             {
40                 Edge[k].num=map[i][j];
41                 Edge[k].x=i;
42                 Edge[k].y=j;
43                 k++;
44             }
45         }
46     }
47     sort(Edge,Edge+k,cmp);
48     dfs();
49     printf("%d",ans);
50     return 0;
51 }

 -----------------------------------------------------------------------------------

P1706 全排列问题

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 int n;
 5 int ans[10];
 6 int vis[10];
 7 void print()
 8 {
 9      for(int i=1;i<=n;i++)
10      {
11          printf("%5d",ans[i]);
12      }
13      printf("\n");
14 }
15 void dfs(int x)
16 {
17    if(x==n) print();
18    for(int i=1;i<=n;i++)
19    {
20        if(!vis[i])
21        {
22            vis[i]=1;
23            ans[x+1]=i;
24            dfs(x+1);
25            vis[i]=0;
26        }
27    }
28 }
29 int main()
30 {
31     scanf("%d",&n);
32     dfs(0);
33     return 0;
34 }

---------------------------------------------------------------------------

AT1350 判断是否可以到达指定的地点:不使用回溯,如果走过,直接标记。

#include <iostream>
#include <cstdio>
using namespace std;
char map[510][510];
int n,m;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int vis[510][510];
int StrX,StrY,EndX,EndY;
bool dfs(int x,int y)
{
    if(x==EndX &&y==EndY)  return true;
    for(int i=0;i<4;i++)
    {
        int xx=x+dx[i],yy=y+dy[i];
        if(map[xx][yy]=='#' || xx<0 ||yy<0 ||xx>=n || yy>=m ||vis[xx][yy]==1) continue;
        vis[xx][yy]=1;
        if(dfs(xx,yy)) return true;
    }
    return false;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
        scanf("%s",map[i]);
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++)
        {
            if(map[i][j]=='s') StrX=i,StrY=j;
            if(map[i][j]=='g') EndX=i,EndY=j;
        }
    }
    vis[StrX][StrY]=1;
    if(dfs(StrX,StrY)) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}

 

 ---------------------CF445A---- 棋子问题-------------------------

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 int n,m;
 5 char map[110][110];
 6 int dx[5]={-1,1,0,0,0};
 7 int dy[5]={0,0,-1,1,0};
 8 inline void dfs(int x,int y,int i)
 9 {
10     int j;
11     if(x<0 ||x>n ||y<0 ||y>=m) return;
12     if(i==0){ map[x][y]='W'; /*cout<<map[x][y]<<endl;*/}
13     if(i==1) map[x][y]='B';
14    // if(x==n && y==m) return;
15    for(j=1;j<=4;j++)
16    {
17            int xx=x+dx[j],yy=y+dy[j];
18           //if(xx<1 || yy<1 || map[xx][yy]=='-' ||xx>n || yy>m ) continue;
19            if(map[xx][yy]=='.')
20            {
21             if(i==0) dfs(xx,yy,1);
22             if(i==1)  dfs(xx,yy,0);
23            }
24 
25    }
26 }
27 int main()
28 {
29     scanf("%d%d",&n,&m);
30     int strX,strY;
31    for(int i=1;i<=n;i++)
32     {
33       scanf("%s",&map[i]);
34     }
35     for(int i=1;i<=n;i++)
36     {
37         for(int j=0;j<m;j++)
38         {
39             if(map[i][j]=='.')
40                 dfs(i,j,1);
41         }
42     }
43     for(int i=1;i<=n;i++)
44         printf("%s\n",map[i]);
45     return 0;
46 }

 -------------------------------------------------------------------------------------------------

P1162颜色填涂 

由数字000组成的方阵中,有一任意形状闭合圈,闭合圈由数字111构成,围圈时只走上下左右444个方向。现要求把闭合圈内的所有空间都填写成222.例如:6×66 \times 66×6的方阵(n=6n=6n=6),涂色前和涂色后的方阵如下:

0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1

想法:先将输入的所有的0都变成2,然后外圈再围一圈2,这样在1外面的都联通了,再从(0,0)dfs,将外圈的2全部变成0就可以啦
 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 const int MAXN=200;
 5 int map[MAXN][MAXN];
 6 int n;
 7 int vis[MAXN][MAXN];
 8 int dx[4]={0,0,-1,1};
 9 int dy[4]={-1,1,0,0};
10 void dfs(int x,int y)
11 {
12     for(int i=0;i<4;i++)
13     {
14         int xx=x+dx[i],yy=y+dy[i];
15         if(xx>n+1 || xx<0 || yy>n+1 || yy<0 || map[xx][yy]==1 ||vis[xx][yy]==1) continue;
16         map[xx][yy]=0;
17         vis[xx][yy]=1;
18         dfs(xx,yy); 
19         //在这里不进行回溯
20     }
21     return ;
22 
23 }
24 int main()
25 {
26     scanf("%d",&n);
27     for(int i=1;i<=n;i++) //从1开始存
28     {
29         for(int j=1;j<=n;j++)
30         {
31             scanf("%d",&map[i][j]);
32             if(map[i][j]==0) map[i][j]=2;
33         }
34     }
35     for(int i=0;i<n+1;i++)
36     {
37         map[i][0]=2; //添加的第一行
38         map[0][i]=2;//第一列
39         map[n+1][i]=2;//最后一行
40         map[n+1][i]=2; //最后一列·
41 
42     }
43     dfs(0,0);
44     for(int i=1;i<=n;i++)  //从1开始输出,不管外面围墙
45     {
46         for(int j=1;j<=n;j++)
47         {
48             printf("%d ",map[i][j]);
49         }
50         printf("\n");
51     }
52     return 0;
53 }

 -----------------------------------------------------------------------------------------------------------

P1141 01迷宫 

题目描述

有一个仅由数字000与111组成的n×nn \times nn×n格迷宫。若你位于一格0上,那么你可以移动到相邻444格中的某一格111上,同样若你位于一格1上,那么你可以移动到相邻444格中的某一格000上。

你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。

输入格式

111行为两个正整数n,mn,mn,m。

下面nnn行,每行nnn个字符,字符只可能是000或者111,字符之间没有空格。

接下来mmm行,每行222个用空格分隔的正整数i,ji,ji,j,对应了迷宫中第iii行第jjj列的一个格子,询问从这一格开始能移动到多少格。

输出格式

mmm行,对于每个询问输出相应答案。

输入输出样例

输入 #1 
2 2
01
10
1 1
2 2
输出 #1
4
4

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <queue>
 4 using namespace std;
 5 const int MAXM=100000;
 6 const int MAXN=1000;
 7 int n,m;
 8 char map[MAXN][MAXN];
 9 int dx[4]={0,0,-1,1};
10 int dy[4]={-1,1,0,0};
11 int vis[MAXN][MAXN];
12 int step[10000005][2];
13 int ans;
14 void dfs(int x,int y)
15 {
16     if(x<0 || y<0 ||x>=n ||y>=n) return;
17     if(vis[x][y]) return;
18     step[ans][0]=x; //将点存储
19     step[ans][1]=y;
20     vis[x][y]=1;
21     ans++;
22     for(int i=0;i<4;i++)
23     {
24         int xx=x+dx[i],yy=y+dy[i];
25         if(map[xx][yy]==map[x][y]) continue; //如果是相同的两个点,则跳过
26         dfs(xx,yy);
27     }
28 }
29 
30 int main()
31 {
32     int a,b;
33     scanf("%d%d",&n,&m);
34     for(int i=0;i<n;i++)
35         scanf("%s",map[i]); //存图
36     for(int i=0;i<m;i++)
37     {
38         scanf("%d%d",&a,&b);
39         a=a-1;b=b-1;// 因为存图的时候是从(0,0)开始的,所以在搜索某一点时也需要减1
40         ans=0;
41         if(vis[a][b]>0)
42             printf("%d\n",vis[a][b]); //如果vis>0 说明已经访问过了,并且已经存过了
43         if(vis[a][b]==0) 
44         {
45             dfs(a,b); //如果没有访问过,搜索一遍
46             for(int i=0;i<ans;i++)
47                 vis[step[i][0]][step[i][1]]=ans; //将它便利的连通的点的访问数组都存为答案;
48             printf("%d\n",ans);
49         }
50 
51     }
52     return 0;
53 }

用step数组存一个连通图的所有的点。一次只能存一个连通图的,最后将此连通图中的结点的vis数组都存储上ans;

这道题一开始想用模板套。但是发现如果用模板套的话,并没有算上当前点。

-------------------------------------------------------

PCF55B

题意翻译

现有4个整数(均小于等于1000),并给出三个运算符(均为+或*)。要求每次取出不一定相邻的两个数,并依次使用给出的运算符对这两个数进行运算,并将结果当做一个新数如此操作,直到只剩下一个数为止。 编程求出最后剩下数的最小值。 翻译贡献者UID:22930

输入输出样例

输入 #1
1 1 1 1
+ + *
输出 #1
3
输入 #2
2 2 2 2
* * +
输出 #2
8
输入 #3
1 2 3 4
* + +
输出 #3
9
#include <iostream>
#include <cstdio>
#include <queue>
#define ll long long
using namespace std;
int INF=0x7f7f7f7f7f7f7f7f;
ll ans=INF;
ll a[5];
char b[4];
void dfs(const int &l)
{
    if(l==1)
    {
        ll s;
        for(int i=1;i<=4;i++)
        {
            if(a[i]!=-1)
            { s=a[i];
              break;
            }//   找最后剩下的那个数
        }
        ans=min(ans,s);
        return;
    }
    for(int i=1;i<=4;i++)
    {
        for(int j=i+1;j<=4;j++)
        {
            int k=4-l+1;
            ll x=a[i],y=a[j];
            char t=b[k];
            //cout<<"x :"<<x<<" y :"<<y<<" t: "<<t<<endl;
            if(x==-1 || t=='-' ||y==-1 ) continue;
            a[j]=-1,b[k]='-';
            if(t=='+') a[i]=x+y;
            else a[i]=x*y;
            dfs(l-1);
            //cout<<"xx :"<<x<<" xy :"<<y<<" tt: "<<t<<endl;
            a[i]=x,a[j]=y,b[k]=t;
            //回溯
           // cout<<"xxx :"<<x<<" xyx :"<<y<<" xtt: "<<t<<endl;
        }
    }
}
int main()
{
    for(int i=1;i<=4;i++)
    scanf("%l64d",&a[i]);
    getchar();
    //cout <<a[3]<<" "<<a[1]<<" "<<a[2]<<" "<<a[4]<<endl;
    for(int i=1;i<=3;i++)
    {
        scanf("%c",&b[i]);
        getchar();
    }
    dfs(4);
    cout<<ans;

    return 0;
}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM