POJ 1190 生日蛋糕

本文分享了一次搜索加剪枝算法的实现经历,作者通过解决一道经典题目,逐步探索并优化了搜索算法,最终实现了有效的剪枝策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

#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的那一刹感觉那一切都值了】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值