dfs进化史

一:简单题

1.二维数组存储

 

P2089 烤鸡 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

一个简单的dfs,虽然很裸但是对我(菜鸡)还是有难度。。需要注意的是要求先输出个数再输出方案数,所以我们用个二维数组把存起来,再for循环输出。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int n,a[15],b[100010][15];
 5 int ans;
 6 
 7 void dfs(int u,int sum)
 8 {
 9     if(u>10)
10     {
11         if(sum==n)
12         {
13             ans++;
14             for(int i=1;i<=10;i++)b[ans][i]=a[i];
15         }
16         return ;
17     }
18     
19     for(int i=1;i<=3;i++)
20     {
21         if(sum+i>n)break;
22         a[u]=i;
23         dfs(u+1,sum+i);
24         a[u]=0;
25     }
26 }
27 
28 int main()
29 {
30 
31     scanf("%d",&n);
32     if(n>30||n<10)printf("0\n");
33     else
34     {
35         dfs(1,0);
36         printf("%d\n",ans);
37         for(int i=1;i<=ans;i++)
38         {
39             for(int j=1;j<=10;j++)printf("%d ",b[i][j]);
40             printf("\n");
41         }    
42             
43     }
44     
45     return 0;
46 } 
1

 

2.全排列

 

 

P1618 三连击(升级版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 

很简单的一道裸题,全排列就好

 

 

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int a1,b1,c1,flag;
 5 int st[15],a[15];
 6 
 7 void dfs(int u)
 8 {
 9     if(u>9)
10     {
11         double fi=a[1]*100+a[2]*10+a[3];
12         double se=a[4]*100+a[5]*10+a[6];
13         double la=a[7]*100+a[8]*10+a[9];
14         cout<<fi<<" "<<se<<" "<<la<<endl;
15         if(fi/a1==se/b1&&fi/a1==la/c1)
16         {
17             printf("%d %d %d\n",(int)fi,(int)se,(int)la);
18             flag=1;
19         }
20         return;
21     }
22     
23     for(int i=1;i<=9;i++)
24     {
25         if(!st[i])
26         {
27             st[i]=1;a[u]=i;
28             dfs(u+1);
29             st[i]=0;a[u]=0;
30         }
31     }
32 }
33 
34 int main()
35 {
36     scanf("%d%d%d",&a1,&b1,&c1);
37     dfs(1);
38     if(!flag)printf("No!!!\n");
39     
40     return 0;
41 }
2

 

3.全排列去重递增

 

 

如果简单的用全排列,会有重复的情况:1 2 3和1 3 2是一样的,所以需要去重,使得排列出的是递增的。

去重方法:在dfs for循环中int i=fi,fi是上个递归中的a [ i ] 的 i + 1,这样for循环永远都是递增的了。

 

 P1036 [NOIP2002 普及组] 选数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N=30;
 5 int a[N],b[N];
 6 bool st[N];
 7 int n,k,ans,j;
 8 
 9 bool primes(int m)
10 {
11     if(m==1)return 0;
12     if(m==2||m==3)return 1;
13     if(m%6!=1&&m%6!=5)return 0;
14     
15     int tmp=sqrt(m);
16     for(int i=5;i<=tmp;i+=6)
17         if(m%i==0||m%(i+2)==0)return 0;
18             
19     return 1;
20 }
21 
22 void dfs(int u,int sum,int fi)
23 {
24     if(u>k)
25     {
26         if(primes(sum))
27             ans++;
28         return;
29     }
30     
31     for(int i=fi;i<=n;i++)
32     {
33         dfs(u+1,sum+a[i],i+1);    //i+1是为了去重递增排列 
34     }
35 
36 }
37 
38 int main()
39 {
40     scanf("%d%d",&n,&k);
41     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
42     
43     dfs(1,0,1);
44     printf("%d\n",ans);
45     
46     return 0;
47 }
3

 4.全排列固定第一次的顺序,求要找的顺序

 

 

P1088 [NOIP2004 普及组] 火星人 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

重点是已知全排列的一种顺序,求之后的某一个顺序。为了节省时间,我们应该在dfs第一次循环的时候就直指已知的那个顺序,然后继续for循环找到要求的顺序。

所以for循环中应写

for ( int  i = 1 ; i < = n ; i + + )
{
  if ( ! flag ) i = a [ u ] ; //第一次循环,直指火星人给的答案
  if ( ! st [ i ] ) 
  {
    st[i]=1;a[u]=i;
    dfs(u+1);
    st[i]=0;a[u]=0;
  }
}

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N=1e4+100l;
 5 int a[N];
 6 bool st[N];
 7 int n,m,flag,flagx;
 8 
 9 void dfs(int u)
10 {
11     if(flagx)return;    //找到人类答案后直接无限返回 
12     if(u>n)
13     {
14         flag++;
15         if(flag==m+1)
16         {
17             for(int i=1;i<=n;i++)printf("%d ",a[i]);
18             printf("\n");
19             flagx=1;        
20         }
21         return;
22     }
23     for(int i=1;i<=n;i++)
24     {
25         if(!flag)i=a[u];    //第一次循环,直指火星人给的答案 
26         if(!st[i])
27         {
28             st[i]=1;a[u]=i;
29             dfs(u+1);
30             st[i]=0;a[u]=0;
31         }
32     }
33     
34 }
35 int main()
36 {
37     scanf("%d%d",&n,&m);
38     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
39     dfs(1);
40     return 0;
41 }
4

 

二:中等题

1.

 

 

P3392 涂国旗 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

dfs ( int x , int  y , int  sum ) , x 表示 w 占了前几行 ,y 表示 r 占了最后几行,sum 表示目前状态 w 与r 需要更改的格子数,在 dfs 中将【x + 1,n - y】的B需要更改的格子数累加,求答案的最小值

注意剪枝:用二维数组st【x,y】表示w占前x行,r占最后y行的状态已被求过,排除重复方案,缩短时间。

思考量最大的点:

dfs(x+1,y,sum+a[x+1][1]);
dfs(x,y+1,sum+a[n-y][3]);

 

注意递归的顺序,先把所有x的情况递归到终止条件,然后再返回每种状态,将y增加(见缝插针),符合要求的就进行下一步。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N=60;
 5 int a[N][5],st[N][N];
 6 int n,m,ans=0x3f3f3f3f;
 7 
 8 void dfs(int x,int y,int sum)
 9 {
10     if(x+y>=n)return;
11     
12     if(st[x][y])return;    //剪枝,减少重复方案 
13     st[x][y]=1;
14     
15     dfs(x+1,y,sum+a[x+1][1]);
16     dfs(x,y+1,sum+a[n-y][3]);
17     if(x!=0&&y!=0&&x+y<n)
18     {
19         int t=0;
20         for(int i=x+1;i<=n-y;i++)
21             t+=a[i][2];
22         ans=min(ans,sum+t);
23     }
24 }
25 
26 int main()
27 {
28     cin>>n>>m;
29     for(int i=1;i<=n;i++)
30     {
31         for(int j=1;j<=m;j++)
32         {
33             char c;
34             //scanf("%c",&c);    //用scanf有空格回车 影响结果 
35             cin>>c;
36             if(c=='W')a[i][2]++,a[i][3]++;
37             else if(c=='B')a[i][1]++,a[i][3]++;
38             else if(c=='R')a[i][1]++,a[i][2]++;
39         }
40     }
41     
42     dfs(0,0,0);
43     cout<<ans<<endl;
44     
45     return 0;
46 }
1

 

2.

 

  

  思路:用dfs对向下和向右分别搜索。

  新知识:可以遍历每个位置,分析每个位置是否满足条件,若满足条件,则向下和向右搜索。

 

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int n,m,k,ans;
 5 char a[120][120];
 6 int dx[2]={0,1},dy[2]={1,0}; 
 7 
 8 void dfs(int x,int y,int t,int sum)
 9 {
10     if(sum>k)    //搜索结束条件 
11     {
12         ans++;
13         return;
14     }
15     if(x>=n||y>=m||a[x][y]!='.')return ;    //搜索过程的边界 
16     dfs(x+dx[t],y+dy[t],t,sum+1);
17     
18 }
19 
20 int main()
21 {
22     scanf("%d%d%d",&n,&m,&k);
23     for(int i=0;i<n;i++)
24         scanf("%s",a[i]);
25     
26     
27     for(int i=0;i<n;i++)
28     {
29         for(int j=0;j<m;j++)
30         {
31             if(a[i][j]=='.')
32             {
33                 for(int t=0;t<=1;t++)dfs(i,j,t,1);    //t==0为向下,t==1为向右 
34             }
35         }
36     }
37     if(k==1)ans/=2;        //特判一个人的时候 
38     printf("%d\n",ans);
39     
40     return 0;
41 }
View Code

 

posted @ 2022-02-04 19:58  wellerency  阅读(46)  评论(0编辑  收藏  举报