POJ 1190 生日蛋糕

来源:互联网 发布:莎莎源码网 编辑:程序博客网 时间:2024/05/18 03:58

呵呵,这道题弄死我。。。

我觉得吧,凡事不能急于求成,比如这道经典的剪枝+搜索,我就先查的题解,看了各种剪枝方法踌躇满志,结果花了两个小时写的剪枝把自己都搞晕了。。。应该先有正确的搜索方法再谈剪枝不是吗,所以我决定先用朴素的搜索做一下

#include<stdio.h>#include<math.h>int n, m;int temp_s = 99999;void search( int a, int r, int h, int v, int s);int main(void){int root;scanf("%d%d", &n, &m);root = sqrt(n);// 这是我设置的一个上限,事实上不需要从n-1开始枚举 search( m, root, root, 0, 0);                // 在这里如果进入递归的参数a值是1,再向上判断就会丢失最优解 printf("%d", temp_s);return 0;}void search( int a, int r, int h, int v, int s){int max_h, i, j;if( a <= 0){if( v == n && temp_s > s){temp_s = s;}return;}for( i = r - 1; i >= a; i--){max_h = ( n - v) / ( i * i);for( j = max_h; j >= a; j--){if( v + i * i * j > n){         // 一个比较简单的优化,大于原体积就不用枚举了 break;}if( a == m) s = i * i;search( a-1, i, j, v+i*i*j, s+2*i*j);    // 这里是作为新手的我一开始没有想到的(事实上这里的v,s随i,j变化,所以作为参数传递很合理)}}}
抱着侥幸的心理提交了一下,呵呵,果真超时了

好吧认命进行剪枝

根据http://www.xuebuyuan.com/738138.html 查的,初步确定以下几个剪枝想法

1、第a层的 r 和 h 至少是 a;可以求出每层的最小体积和表面积,如果a-1层的 v , s 使剩余体积小于合法能够达到的最小体积,则说明a-1层体积过小,由于是递减循环,所以这说明这一个分支已经可以不用继续搜索了

2、如果剩余体积加最小体积大于标准体积,那么这个分支也不用继续搜索了

3、如果在没有到递归底部表面积就大于现有的最优解,那么这个分支也不用继续搜索了

4、如果这一次加下一次的最小表面积大于现有最优解,那么这个分支也不用继续搜索了(可以和4合并)

#include<stdio.h>#include<math.h>#define min(a,b) (a > b ? b : a)        // 这里的写法挺好的,免得再写一个函数了 int n, m;int temp_s = 999999;// temp_s 设置一个很大的值 int min_v[25], min_s[25]; void search( int a, int r, int h, int v, int s);// 搜索函数 void minset( int m);// 求最小值函数 int main(void){int root;while(~scanf("%d%d", &n, &m)){        // 一个确保连续输入的语句 ~按位取反 minset(m);root = sqrt(n);search( m, root, root, 0, 0);        // 这里root应该能缩小一些搜索范围 // 题意是一个倒三角形,所以从这里开始相当于一个从顶向下的递归(顶最大,越向下越小)if( temp_s == 99999)  temp_s = 0;        // 这里表示没有可能值 printf("%d\n", temp_s);}return 0;}void minset( int m){int i;min_v[0] = min_s[0] = 0;for( i = 1; i <= m; i++){min_v[i] = min_v[i-1] + i*i*i;       // 每一层的体积最小值 min_s[i] = min_s[i-1] + 2*i*i;       // 每一层的表面积最小值 }}void search( int a, int r, int h, int v, int s){int max_h, i, j;if( a == 0){if( v == n && temp_s > s) temp_s = s;               // 若小于最优解就替换 return;}if( s + min_s[a-1] >= temp_s || v + min_v[a-1] > n || 2*(n-v)/r + s >= temp_s) return;// 第一个条件即判断:若此次体积加最小体积大于标准体积,放弃这个分支// 第二个条件即判断:若此次加下一次的最小表面积大于现有最优解,放弃这个分支// 第三个条件即判断:若剩余体积所需的最小表面积加已有的表面积大于最优解,放弃这个分支。貌似没有这个判断就会超时         // <span style="color:#3333FF;">我觉得这个题的剪枝包括两个方面,第一个方面是对下一个状态进行判断,第二个方面是对远景状态进行判断,其中第二个方面能筛下去很多很多不必要分支</span>for( i = r - 1; i >= a; i--){max_h = min((n-v-min_v[a-1]) / ( i * i), h-1);// 求h-1和 剩余体积与最小体积之差 之间的最小值——<span style="color:#FF0000;">没有这个会WA...并不知道为什么</span> for( j = max_h; j >= a; j--){if( a == m) s = i * i;       // 还有一个圆的面积 search( a-1, i, j, v+i*i*j, s+2*i*j);               // v,s作为参数传递 }}}

【恩,头一个半自主完成的搜索+剪枝题,虽然前前后后包括读题和写博客总结花了我将近五个小时,超时WA了无数次,但是做完还是很高兴的,AC的那一刹感觉那一切都值了】

0 0
原创粉丝点击