题意 : 有一个多层蛋糕,已知蛋糕的体积和层数,求该蛋糕的最小表面积(最底层的下表面面积不计入总面积)
题目对每一层的半径和高度都有限制,下层的半径和厚度要大于上层的半径和厚度 Ri > Ri-1, Hi > Hi-1,且半径和厚度均为整数
很好的一道深搜配合剪枝优化的题目,其中的几类剪枝优化很有代表性。
剪枝1. 无论是搜索最优解还是搜索确定解,我们总希望尽快的找到解,因此就要调整搜索树的遍历顺序。
下图表示一颗搜索树,绿色节点表示解所在的路径。遍历图一中的搜索树会比较晚的得到解,而遍历调整搜索顺序后的搜索树会较快的得到解。
怎样调整搜索顺序?我们需要优先搜索那些可能很快就能得到解的子问题,往往可以通过对子问题进行排序来达到调整搜索顺序的目的。
这样就可以比较早的得到问题的解或最优解。如果要求一个确定的解,到此就结束搜索了,可以有效减小搜索宽度;如果要求一个最优解,还需与另一个强有力的剪枝配合。
下面一类重要剪枝需要利用极限的思想。
(说明:下面将最优解(或近似最优解)和其他搜索目标统称为搜索目标。因为有时搜索目标可能有多个,例如该题搜索目标就有两个,总体积和最小表面积,其中后者为我们要找的最优解。)
剪枝2. 通过剪枝1可以较早的得到一个搜索目标,在接下来对每一个子问题进行搜索前可以先获得该子问题的搜索目标的极限,通过该极限可以判断对该子问题搜索出的解能否比当前最优解更优;或对该子问题搜索出的搜索目标能否满足要求,如果不能,就没必要了搜下去了,直接跳过对该子问题的搜索。该剪枝可以有效减小对某一子问题的搜索深度。
- // 1190 生日蛋糕.cpp : 定义控制台应用程序的入口点。
- //
- #include <iostream>
- #include <cmath>
- using namespace std;
- const int oo = 999999999;
- // 对象
- int V, M;
- int minS;
- int leftS[21], leftMinV[21];// leftS[i]表示第i层及其之上各层的最小表面积
- // leftMinV[i]表示第i层及其之上各层的最小体积
- // 函数
- void Init()
- {
- memset( leftS, 0, sizeof(leftS) );
- memset( leftMinV, 0, sizeof(leftMinV) );
- for( int m = 1; m <= 20; ++ m )
- for( int i = 0; i < m; ++ i ) {
- leftS[m] += (m-i) * (m-i);
- leftMinV[m] += (m-i) * (m-i) * (m-i);
- }
- }
- /*
- * 参数 :
- * v : 剩余体积,即还需靠搜索来拼凑的体积
- * m : 剩余层数,....................层数
- * lastR, lastH : 下层的半径和高度
- * sumS : 已搜索出的表面积和
- */
- void DFS( const int v, const int m, const int lastR, const int lastH, int sumS )
- {
- if( m == 0 && v == 0 ) {
- if( sumS < minS )
- {
- minS = sumS;
- }
- return;
- }
- if( leftS[m] + sumS >= minS ) return; // 极限的思想,限制搜索深度
- if( leftMinV[m] > v ) return; // 极限的思想,限制搜索深度
- int avgV = v / m;
- for( int r = lastR - 1; r >= m; -- r ) { // 优先找到最优解
- for( int h = lastH - 1; h >= m; -- h ) {
- int curV = r * r * h;
- if( curV < avgV ) break;
- if( curV > v ) continue;
- int leftMaxV = 0;
- for( int i = 1; i < m; ++ i )
- leftMaxV += (r-i) * (r-i) * (h-i);
- if( v - curV > leftMaxV ) break; // 极限的思想,限制搜索深度
- if( m == M ) sumS = r * r;
- DFS( v - curV, m - 1, r, h, sumS + 2*r*h );
- }
- }
- }
- int main()
- {
- Init();
- scanf( "%d%d", & V, & M );
- minS = oo;
- DFS( V, M, sqrt((double)V) + 1, V + 1, 0 );
- int res = minS < oo ? minS : 0;
- printf( "%d\n", res );
- system( "pause" );
- return 0;
- }