高级算法--深搜
下面是一本通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 */
I can do all things