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