Dfs学习经验(纸上运行理解DFS)【两道题】

  首先我想吐槽的是,在CSDN上搞了好久还是不能发博客,就是点下发表丝毫反应都没有的,我稍微百度了几次还是没有找到解决方法,在CSDN的BBS上也求助过管理员但是没有收到答复真是烦躁,导致我新生入学以来没能很好的在博客上记录点什么,有些想法只是在脑中灵光一现然后事后就不好想起来了。后来发现了除了CSDN还有ITeye、cnblogs等一些其他优秀的博客网站…。废话不多说了,现在开始吧。

 

背景:在大一上学期的第六次C语言课后练习中碰到了这样一题

 

放苹果(POJ1664

题目描述

把M个同样的苹果放在N个同样的盘子里,允许有盘子空,问共有多少种不同分法
注意:5,1,1和1,5,1是同一种。

输入

第一行是测试数据数目t(0~20),以下t行均包含两个整数M,N,空格分开。
1<=M,N<=10;

输出

对每行数据,输出相应的k。

样例输入

1

7 3

样例输出

8

 

审题思考:

在经过小范围数据的纸上运行后,发现了这么一个规律:

0 0 0

0 0 7 OK

0 1 1

0 1 6 OK

0 2 2

0 2 5 OK

0 3 3

0 3 4 OK

0 4 4

0 5 5

0 6 6

0 7 7

1 1 1

1 1 5 OK

……

于是,按照这个规律,我试着这么写:

当M=7,N=3的时候:

 1 if(N==3)
 2             {
 3                 for(i=0;i<M;i++)
 4                 {
 5                     for(j=i;j<M;j++)
 6                     {
 7                         for(k=j;k<=M;k++)
 8                         {
 9                             if((i+j+k)==M)
10                             {
11                                 ti++;
12                                 break;
13                             }
14                         }
15                     }
16                 }
17                 printf("%d
",ti);
18             }

同理,N在1-10这10个数中,都可以利用此种多重FOR循环嵌套进行搜索判断

按照这种规律,我完整的写了一遍这题,但是发现提交上去WA

以下是我完整的代码:

  1 #include<stdio.h>
  2 int main()
  3 {
  4     int n,i,j,k,M,N,ti,i4,i5,i6,i7,i8,i9,i10;
  5     while(scanf("%d",&n)!=EOF)
  6     {
  7         while(n--)
  8         {
  9             ti=0;
 10             scanf("%d%d",&M,&N);
 11             if(N==1)
 12                 printf("1
");
 13             if(N==2)
 14             {
 15                 for(j=0;j<M;j++)
 16                 {
 17                     for(k=j;k<=M;k++)
 18                     {
 19                         if((j+k)==M)
 20                         {
 21                             ti++;
 22                             break;
 23                         }
 24                     }
 25                 }
 26                 printf("%d
",ti);
 27             }
 28             if(N==3)
 29             {
 30                 for(i=0;i<M;i++)
 31                 {
 32                     for(j=i;j<M;j++)
 33                     {
 34                         for(k=j;k<=M;k++)
 35                         {
 36                             if((i+j+k)==M)
 37                             {
 38                                 ti++;
 39                                 break;
 40                             }
 41                         }
 42                     }
 43                 }
 44                 printf("%d
",ti);
 45             }
 46             if(N==4)
 47             {
 48                 for(i4=0;i4<M;i4++)
 49                 {
 50                     for(i=i4;i<M;i++)
 51                     {
 52                         for(j=i;j<M;j++)
 53                         {
 54                             for(k=j;k<=M;k++)
 55                             {
 56                                 if((i4+i+j+k)==M)
 57                                 {
 58                                     ti++;
 59                                     break;
 60                                 }
 61                             }
 62                         }
 63                     }
 64                 }
 65                 printf("%d
",ti);
 66             }
 67             if(N==5)
 68             {
 69                 for(i5=0;i5<M;i5++)
 70                 {
 71                       for(i4=i5;i4<M;i4++)
 72                     {
 73                         for(i=i4;i<M;i++)
 74                         {
 75                             for(j=i;j<M;j++)
 76                             {
 77                                 for(k=j;k<=M;k++)
 78                                 {
 79                                     if((i5+i4+i+j+k)==M)
 80                                     {
 81                                         ti++;
 82                                         break;
 83                                     }
 84                                 }
 85                             }
 86                         }
 87                     }
 88                 }
 89                 printf("%d
",ti);
 90             }
 91             if(N==6)
 92             {
 93                 for(i6=0;i6<M;i6++)
 94                 {
 95                       for(i5=i6;i5<M;i5++)
 96                     {
 97                           for(i4=i5;i4<M;i4++)
 98                         {
 99                             for(i=i4;i<M;i++)
100                             {
101                                 for(j=i;j<M;j++)
102                                 {
103                                     for(k=j;k<=M;k++)
104                                     {
105                                         if((i6+i5+i4+i+j+k)==M)
106                                         {
107                                             ti++;
108                                             break;
109                                         }
110                                     }
111                                 }
112                             }
113                         }
114                     }
115                 }
116                 printf("%d
",ti);
117             }
118             if(N==7)
119             {
120                 for(i7=0;i7<M;i7++)
121                 {
122                     for(i6=0;i6<M;i6++)
123                     {
124                           for(i5=i6;i5<M;i5++)
125                         {
126                               for(i4=i5;i4<M;i4++)
127                             {
128                                 for(i=i4;i<M;i++)
129                                 {
130                                     for(j=i;j<M;j++)
131                                     {
132                                         for(k=j;k<=M;k++)
133                                         {
134                                             if((i7+i6+i5+i4+i+j+k)==M)
135                                             {
136                                                 ti++;
137                                                 break;
138                                             }
139                                         }
140                                     }
141                                 }
142                             }
143                         }
144                     }
145                 }
146                 printf("%d
",ti);
147             }
148             if(N==8)
149             {
150                 for(i8=0;i8<M;i8++)
151                 {
152                     for(i7=0;i7<M;i7++)
153                     {
154                         for(i6=0;i6<M;i6++)
155                         {
156                               for(i5=i6;i5<M;i5++)
157                             {
158                                   for(i4=i5;i4<M;i4++)
159                                 {
160                                     for(i=i4;i<M;i++)
161                                     {
162                                         for(j=i;j<M;j++)
163                                         {
164                                             for(k=j;k<=M;k++)
165                                             {
166                                                 if((i8+i7+i6+i5+i4+i+j+k)==M)
167                                                 {
168                                                     ti++;
169                                                     break;
170                                                 }
171                                             }
172                                         }
173                                     }
174                                 }
175                             }
176                         }
177                     }
178                 }
179                 printf("%d
",ti);
180             }
181             if(N==9)
182             {
183                 for(i9=0;i9<M;i9++)
184                 {
185                     for(i8=0;i8<M;i8++)
186                     {
187                         for(i7=0;i7<M;i7++)
188                         {
189                             for(i6=0;i6<M;i6++)
190                             {
191                                   for(i5=i6;i5<M;i5++)
192                                 {
193                                       for(i4=i5;i4<M;i4++)
194                                     {
195                                         for(i=i4;i<M;i++)
196                                         {
197                                             for(j=i;j<M;j++)
198                                             {
199                                                 for(k=j;k<=M;k++)
200                                                 {
201                                                     if((i9+i8+i7+i6+i5+i4+i+j+k)==M)
202                                                     {
203                                                         ti++;
204                                                         break;
205                                                     }
206                                                 }
207                                             }
208                                         }
209                                     }
210                                 }
211                             }
212                         }
213                     }
214                 }
215                 printf("%d
",ti);
216             }
217             if(N==10)
218             {
219                 for(i10=0;i10<M;i10++)
220                 {
221                     for(i9=0;i9<M;i9++)
222                     {
223                         for(i8=0;i8<M;i8++)
224                         {
225                             for(i7=0;i7<M;i7++)
226                             {
227                                 for(i6=0;i6<M;i6++)
228                                 {
229                                       for(i5=i6;i5<M;i5++)
230                                     {
231                                           for(i4=i5;i4<M;i4++)
232                                         {
233                                             for(i=i4;i<M;i++)
234                                             {
235                                                 for(j=i;j<M;j++)
236                                                 {
237                                                     for(k=j;k<=M;k++)
238                                                     {
239                                                         if((i10+i9+i8+i7+i6+i5+i4+i+j+k)==M)
240                                                         {
241                                                             ti++;
242                                                             break;
243                                                         }
244                                                     }
245                                                 }
246                                             }
247                                         }
248                                     }
249                                 }
250                             }
251                         }
252                     }
253                 }
254                 printf("%d
",ti);
255             }
256         }
257     }
258     return 0;
259 }
View Code

但是对于用这种方法解决这题我并不是很满意,虽然不知道为什么WA了。

在查阅了相关资料后发现可以利用DFS的方式

这里有一段关于解决这题的DFS相关函数:

 1  1 //代码来西电ACMPPT
 2  2 void dfs(long k, long w)    //初始k=1,w=0; k表示现在已用几个盘子,w表示已经放了几个苹果
 3  3 {                                         
 4  4   long i,u; 
 5  5   if (k==m)                     //当最后一个盘子被放置苹果的时候我们进行判断
 6  6     { 
 7  7         if (n-w>=s[k-1]) 
 8  8             { 
 9  9                s[k]=n-w; 
10 10                z=z+1; 
11 11             }
12 12         return; 
13 13     }
14 14          for (i=0;i<=n;i++) 
15 15              if (i>=s[k-1])           //如果当前放置的苹果个数大于前一个盘子继续放置
16 16                { 
17 17                   w=w+i; 
18 18                   s[k]=i; k=k+1; 
19 19                    dfs(k,w); 
20 20                   w=w-i; 
21 21                   k=k-1; 
22 22                } 
23 23 } 
View Code

当初看到这段简短的代码,我只是一眼看出了这是与递归相关的函数

花了一段时间纸上运行后(在纸上写出详细步骤)

大致写出我的纸上运行内容:

以实例输入为5个苹果,3个盘子

Start: k=1,w=0

第一次进入DFS函数

W=0,s[1]=0,k=2;

第二次进入DFS函数

W=0,s[2]=0,k=3;

第三次进入DFS函数

S[3]=5,z=z+1;

退出第三次进入的DFS函数,返回第二次进入的DFS函数

I=1,w=1,s[2]=1,k=3

第三次进入DFS函数

S[3]=4,z=z+1;

退出第三次进入的DFS函数,返回第二次进入的DFS函数

W=0,k=2;

I=2,w=1,s[2]=2,k=3;

第三次进入DFS函数

S[3]=3,z=z+1

退出第三次进入的DFS函数,返回第二次进入的DFS函数

!暂停

 

在此,我们已经得到三组答案数据,即 0 0 5、 0 1 4和 0 2 3

并且得到Z=3;

那么按照这个规律,我们可以得出一共会有七组答案数据:

0 0 7

0 1 6

0 2 5

0 3 4

1 1 5

1 2 4

1 3 3

2 2 3

 

在此,我们已经可以利用DFS的方法解决这道题目,当然

这样解决的效率并不是很高,所有需要剪枝

优化的代码为:

 1  1 //代码来自西电ACMPPT
 2  2 void dfs(long k, long w)
 3  3 {
 4  4   long i,u;
 5  5   if (k==m)
 6  6     {
 7  7       if (n-w>=s[k-1])
 8  8         {
 9  9           s[k]=n-w;
10 10           z=z+1;
11 11         }
12 12       return;
13 13     }
14 14   for (i=0;i<=n;i++)
15 15     if ((i>=s[k-1])&&((n-w)/(m-k)>=s[k-1]))       //优化剪枝部分
16 16       {
17 17         w=w+i;
18 18         s[k]=i;
19 19         k=k+1;
20 20         dfs(k,w);
21 21         w=w-i;
22 22         k=k-1;
23 23       }
24 24 }
View Code

到此为止,本题结束,但是我还想再提一题:

 

问题 C: 伯努利装错信封问题-综合[难]

时间限制: 30 Sec  内存限制: 128 MB
提交: 417  解决: 199
[提交][状态][讨论版]

题目描述

    某人写了n封信,同时为每一封信写1个信封,共n个信封。如果把所有的信都装错了信封,问共有多少种?(这是组合数学中有名的错位问题。著名数学家伯努利(Bernoulli)曾最先考虑此题。后来,欧拉对此题产生了兴趣,称此题是“组合理论的一个妙题”,独立地解出了此题)

    

    试编程求出完全装错情形的所有方式及其总量s。例如,输入n=3,即有3封信需要装入信封,完全装错的一种方式可以表示为312,表示第1封信装入第3个信封,第2封信装入第1个信封,第3封信装入第2个信封。对于n=3,完全装错的方式共有2种,分别是312和231.

输入

输入一个正整数n(2<=n<=6)

输出

输出完全装错情形的所有方式以及装错方式的总量s (每行输出5种方式,一行中的相邻两种方式之间用1个空格隔开。装错方式输出时,从小到大排列,见输出样例)。

样例输入

4

样例输出

2143 2341 2413 3142 3412

3421 4123 4312 4321

s=9

 

审题思考:

解决这道题目可以利用DFS。

参考过百度知道http://zhidao.baidu.com/link?url=RSi7TcgSite5RRMgHhLLGjrF12CdpX9bYL_XvyRkelMC_5HTsZY1lICYgWxNrYBNQ_xKMJWj627uiCHdgDxmYbys8g39Xdf2Bu_ji430GS7

的回答后,我对代码进行了适当解释和修改,以下是完整代码:

 1 //代码改编自百度知道
 2 #include<stdio.h>
 3 #include<stdlib.h>
 4 int printed,ti=0;
 5 void draw(int* a,int k)//输出函数
 6 {
 7     int i;
 8     for(i=0;i<k;i++)
 9         printf("%d",a[i]);
10     printf(" ");
11     if(ti!=5)
12         ti++;
13     if(ti==5)
14     {
15         printf("
");
16         ti=0;
17     }
18 }
19 void Settle(int *a,int iStep,int k)//函数调用值为:a,0,k
20 {
21     int i;
22     for(i=0;i<iStep-1;i++)
23         if(a[iStep-1]==a[i]) return;//判断序号为(iStep-1)的信是否同序号为i的信放入同一个信箱,否则将函数返回
24     if(iStep==k)
25     {
26         draw(a,k);
27         printed++;
28     }
29     for(i=1;i<=k;i++)
30     {
31         if(i==iStep+1) continue;//判断序号i信是否放到的是i信封,是则继续下一步
32         a[iStep]=i;
33         Settle(a,iStep+1,k);
34     }
35 }
36 int main()
37 {
38     int* a;
39     int k;
40     scanf("%d",&k);
41     a=(int*)calloc(k,sizeof(int));//在内存的动态存储区中分配k个长度为int大小的连续空间
42     Settle(a,0,k);
43     printf("
s=%d",printed);
44     return 0;
45 }

 

  a=(int*)calloc(k,sizeof(int));//在内存的动态存储区中分配k个长度为int大小的连续空间
 /*
     它的意思是a为一块长度为k*sizeof(int) = 4k的内存块,可以存储k个int型变量,这个内存块就相当于一个int数组
 也就是等价于
 int a[k]; // 注意:此时a的内存在堆栈(Stack)上面
 不同的是,你所询问的这句,申请的这块内存区域是动态申请的,属于堆(heap)上面的空间。
 
 calloc是一个C语言函数
   函数名: calloc
   void *calloc(unsigned n,unsigned size);
   功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
   跟malloc的区别:
   calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。
   用 法: void *calloc(unsigned n,unsigned size);
   头文件:stdlib.h或malloc.h
 */

为了解读这串代码我同样在纸上运行了一小段数据帮助理解

以下是我在纸上运行的过程:

以输入N=4 为例

 

第一次调用settle函数

a : 空 空 空 空 , istep=0,k=4;

i=1,i=2,a[0]=2;

第二次调用settle函数

a : 2 空 空 空 , istep=1,k=4;

i=1,a[1]=1

第三次调用settle函数

a : 2 1 空 空 , istep=2,k=4;

i=1,a[2]=1;

第四次调用settle函数

a : 2 1 1 空 , istep=3,k=4;

i=0,i=1,a[1]=a[2]

函数返回

第三次调用settle函数

a : 2 1 空 空 , istep=2,k=4;

i=2,a[2]=2

第四次调用settle函数

a : 2 1 2 空 , istep=3,k=4;

i=0,a[2]=a[0]

函数返回

第三次调用settle函数

a : 2 1 空 空 , istep=0,k=4;

………(省略数步骤)

第五次调用settle函数

a : 2 1 4 3 , istep=4,k=4;

输出2143

 

经过这样的纸上运行后,发现DFS在编写程序上以递归的形式,可以通过简短的代码解题

但是面临的问题是:

1、  代码难写,不好控制终止等条件

2、  执行效率低(目前我对剪枝的原理还不是很懂)

 

总结:

相比于我曾经完全利用的多重FOR循环嵌套语句,DFS递归的方式更体现出了程序的严密和递归的好处

原文地址:https://www.cnblogs.com/wushuaiyi/p/3454352.html