高级算法--深搜

下面是一本通OJ上题目的代码实现

·例一:

题目描述:

将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。

例如:n=7,k=3,下面三种分法被认为是相同的。

1,1,5; 1,5,1; 5,1,1;

问有多少种不同的分法。 输出一个整数,即不同的分法。

代码实现:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 int n,k;
 5 int f[210][7];
 6 int main()
 7 {
 8     scanf("%d%d",&n,&k);
 9     memset(f,0,sizeof(f)); //f数组先初始化为0,
10     f[0][0]=1;//但是第一个要为1,为了防止加来加去都是0
11     for(int i=1;i<=n;i++)//总和的循环
12         for(int j=i;j<=n;j++)//也是总和的循环,因为前面已经循环了i,所以j可以直接由i开始
13         //循环两次是为了后面状态的处理做铺垫
14             for(int x=1;x<=k;x++)//这是循环方案数
15             {
16                 f[j][x]=f[j][x]+f[j-i][x-1];//f[j][x]就是方案总数的最大值
17                 //j-i是为了防止重复,比如说:1 1 5,1 5 1,5 1 1 这样的情况出现
18                 //x-1就是上一个的最大值,是为了继承的
19             }
20     printf("%d\n",f[n][k]);//输出最大值
21     return 0;
22 }

·例二:

题目描述:

  7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。设从下往上数第i(1≤i≤M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i<M时,要求Ri>Ri+1且Hi>Hi+1。由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。

令Q=Sπ,请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。

(除Q外,以上所有数据皆为正整数)

代码实现:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;      
 5 int n,m,minn=2e+9; //min表为面积最小的值,初始化为一个极大值
 6 void dfs(int d,int v,int s,int r,int h)
 7 //前d层蛋糕体积为v 表面积为s 第d层半径r 高度h 
 8 {
 9     if(d==m)//前d层就是m层,表示我们已经搜索完了    
10      {
11         if(v==n)minn=s;//如果体积也符合题目要求的话,更新表面积的最小值 
12         return;//返回值 
13      }
14     if(v+(r-1)*(r-1)*(h-1)*(m-d)<n)return;
15     /*
16     1.如果当前体积加上之后每层的最大值,还比题目要求的体积小,直接结束该趟递归
17     2.我在主函数当中讲了我们的搜索是自下而上,所以下面一层比上面一层要小
18     3.为什么说是最大值呢?因为我们选取的是当前上一层的半径,而且越往上越小
19     而我们乘以的同样也是那么多层,所以说这个是最大值
20     */ 
21     if(v+m-d>n)return;           
22     /*
23     1.如果当前体积加上之后每层的最小值,还比题目要求的体积大,直接结束该趟递归
24     2.为什么说是最小值呢?因为我们的上一层是比下面的那一层要小的,也就是最顶上的
25     可能半径就为1,是最小的,既然是最小的说明下面的就比他要大,但是我们就直接
26     最小半径的平方1*1*(m-d)也就是层数,那么可能会疑惑高去哪里了?
27     高也是假设了最小的1,所以整个半径平方乘以高乘以层数=1*1*1*(m-d)=m-d
28     */
29     if(2*(n-v)/r+s>=minn)return;  
30     /*
31     如果求解过程半途找到比当前最小值,也就是minn的值还大的数据,结束该趟递归
32     这一步是最关键的一步但是我不知道现在在这里怎么表示出来 
33     */ 
34     for(int i=r-1;i>=m-d;i--)
35     /*
36     i(半径)[再上一层的半径]的最小值要保证大于当前这一层半径的最小值
37     题目解释当中说的至少要大1,也就是说上一层的半径最大是当前这一层的半径-1
38     也就是r-1
39     最小的话就是也要大于等于剩下的层数,不然后面的层就没有整数半径
40     比如说:
41     总共有5层,当前是第3层(顺数),第3层的半径是5,
42     那么第2层的半径最大就是4,最小的话就是(5-3)=2
43     因为只有大于等于2的时候,这一层的上一层才有半径,
44     如果第2层的半径是1的话,那么至少要大于等于1,也就是第1层的半径小于等于0
45     这个显然是不可能的 
46     */
47     {
48         for(int j=h-1;j>=m-d;j--)
49         /*
50         j(高度)[再上一层的高度]的最小值要保证大于当前这一层高度的最小值
51         跟半径是同样的道理,这里就不再解释了 
52         */ 
53         {
54             if((i*i*j+v<=n)&&(s+2*i*j<minn))
55             /*如果我们后面找到的这个半径和高度组合的体积小于等于n*/
56             /*并且它的面积比我们之前记录过的要小的话*/
57                 dfs(d+1,v+i*i*j,s+2*i*j,i,j);/*递归搜索子状态,也就是处理下一个*/ 
58         }
59     }
60 }
61 int main()
62 {
63      scanf("%d%d",&n,&m);
64      for(int i=m;i*i*m<=n;i++)
65      /*
66      1.i表示半径,半径的平方(也就是底面积)*层数(也就是体积)
67      小于要求的体积的话,可以继续
68      2.i从m开始是因为,底下每一层的高度和半径至少比上一层的大1,
69      也就是说,最底下一次的半径和高度至少为M了
70      */
71      {
72         for(int j=m;i*i*j<=n;j++)
73         /*
74         1.j表示高度,高度乘以底面积小于要求体积,可以继续
75         2.i从m开始是因为,底下每一层的高度和半径至少比上一层的大1,
76         也就是说,最底下一次的半径和高度至少为M了 
77         */ 
78         {
79             if(i*i+2*i*j<minn)/*小于我们一直更新的最小值,才传入*/ 
80             dfs(1,i*i*j,i*i+2*i*j,i,j);
81             /*
82             从第m层开始,我们之前的枚举是自下而上,所以在搜索中也是自下而上
83             注意:(分开五个来分析) 
84             (1)从前1层开始 
85             (2)体积是半径*半径*高 
86             (3)表面积不单单是侧面积,最底下那一层的表面积 
87             (4)i表示半径
88             (5)j表示高度 
89             */ 
90         }
91     }
92     printf("%d\n",minn);/*输出最小值*/
93     return 0;
94 }
95 /*
96 体积V=πR*R*H
97 侧面积A’=2*π*R*H
98 底面积A=π*R*R
99 */

 

posted @ 2019-06-08 20:50  蒟蒻hqk  阅读(206)  评论(0编辑  收藏  举报