深搜(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