实验目的、内容及要求: 实验目的: 1.掌握回溯法的基本思想 2.使用递归回溯求解实际问题 3.在回溯法中掌握剪枝技巧 实验内容: 要制作一个体积为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外,以上所有数据皆为正整数。 输入格式:输入有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。 输出格式:输出仅一行,是一个正整数S(若无解则S = 0)。 题目来源:POJ1011(http://poj.org/problem?id=1190)) 要求:撰写实验代码,提交到POJ的结果必须是Accept。 |
实验仪器设备(实验环境): 普通主流PC,英特尔I5处理器2.0GHz,8GDDR4内存,500G硬盘,19寸液晶显示器。 |
实验过程(包括实现思路、实验步骤/实现代码、实验结果/运行图): 自下而上搭建蛋糕,因为每一层的半径和高度要求都是整数,当蛋糕有M层,最下面的一层的半径r和高度h指定的前提下,从最下层到最上层,每一层的半径都有一个上界值,分别为r、r-1、r-2、…、r-M+1,每一层的高度也有一个上界值,分别为h、h-1、h-2、…、h-M+1,这样,每一个层的蛋糕的体积也就有了一个上界值。这说明,当蛋糕有M层,最下面的一层的半径r和高度h指定的前提下,蛋糕的体积有一个上界值。 同理,自下而上,每一层的半径都有一个下界值,分别为M、M-1、M-2、…、1,每一层的高度也有一个下界值,分别为M、M-1、M-2、…、1。引入minVi表示自上而下的前i层的体积之和的下界值,引入minAi表示自上而下的前i层的侧面积之和的下界值。则有递推公式: minVi = minVi -1+π*i3; minAi = minAi -1+2*π*i2 注意边界条件minV0 =0, minA0 =0。 每一层的上表面需要涂抹奶油的表面积加起来正好等于最下面一层的下表面,可将底面积作为外表面积的初始值,之后只要累加上每一层的侧面积,使用递归回溯法进行深度优先搜索。 引入DFS(int v, int top, int r, int h)表示搜索过程,其中: v表示还要搭建多少体积的蛋糕; top表示还需要搭建的层数; r表示自下而上搭建时,即将搭建的这一层的半径可能的最大值; h表示自下而上搭建时,即将搭建的这一层的高度可能的最大值。 遇到下列情况,必须剪枝: 1、当top为0但是v不为0时,数据明显不合法; 2、还没搭建完所有的层,蛋糕体积已经超过指定体积; 3、v<minVtop,即还需要搭建的体积不够上面top层的最小体积; 4、已搭建好的蛋糕的部分面积area,加上还没搭建的蛋糕至少需要的表面积minAtop,就已经超过了目前求得的最少表面积; 5、每一层的高度和半径都必须是整数,若h<top或r<top,表示再往上搭建蛋糕,高度已经无法安排,或者半径已经无法安排; 6、需要搭建的共top层的蛋糕体积的上界值小于v,即剩下还没搭建的那部分体积,怎么构造都无法达到还缺少的体积v。 具体代码如下: #include <iostream> #include <algorithm> #include <cstring> using namespace std; int n,sum,num,flag; int a[65],vis[65]; bool cmp(int x,int y){ return x>y; } void dfs(int num1,int len,int pos){ if(flag){ return ; } if(num1==num){ flag=1; return ; } for(int i=pos;i<n;i++){ if(vis[i]) continue; if(sum/num==a[i]+len){ vis[i]=1; //num++运算后num实际取值为num+1,但显示的结果仍num, //下次运算时用num+1的取值运算,但显示num+1的值; //num=num+1的值显示和实际的都为num+1. dfs(num1+1,0,0); vis[i]=0; return ; }else if(sum/num>a[i]+len){ vis[i]=1; dfs(num1,len+a[i],i+1); vis[i]=0; if(len==0) return ; while(a[i]==a[i+1]) i++; } } } int main(int argc, char** argv) { while(scanf("%d",&n)&&n){ sum=0,flag=0; memset(a,0,sizeof(a)); memset(vis,0,sizeof(vis)); for(int i=0;i<n;i++){ cin>>a[i]; sum+=a[i]; } sort(a,a+n,cmp); for(int i=a[0];i<=sum;i++){ if(sum%i) continue; num=sum/i; dfs(0,0,0); if(flag){ cout<<i<<endl; break; } } } return 0; } |
回溯法实验
猜你喜欢
转载自blog.csdn.net/weixin_63747428/article/details/134615908
今日推荐
周排行