生日蛋糕 (codevs 1710) 题解

【问题描述】

7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为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外,以上所有数据皆为正整数)

【样例输入】

100

2

【样例输出】

68

【解题思路】

本题为NOI1999的题目,题目的意思是让我们去搜索合适的r和h,使得s最小。我们把某一步的状态设为(i,ri,hi,si,vi),i表示从下往上数做到了第几层蛋糕,ri表示当前最上层蛋糕的半径,hi表示当前最上层蛋糕的高度,si表示当前蛋糕的表面积,vi表示做了当前蛋糕现在剩下的体积。

于是乎,由题意可知,从(i,ri,hi,si,vi)到(i+1,r(i+1),h(i+1)s(i+1),v(i+1))满足以下条件: r(i+1)<ri;h(i+1)<hi;v(i+1)=vi-r(i+1)*r(i+1)*h(i+1);s(i+1)=si+2*r(i+1)*h(i+1);

直接就这么搜索显然是不现实的,我们需要剪枝,那么,我们可以从下面几个方面想:

1:当前表面积+剩余的侧面积>当前最优值,那么我们就可以剪枝了,关键在于剩余的侧面积该怎么算。这里有一个公式,是由2*vi推出来的,余下的侧面积>=2*vi/r(i+1),因此,如果2*vi/r(i+1)+si大于当前最优值,那么我们就剪枝。这个叫做最优化剪枝。

2:若剩余的体积比做最小的蛋糕的体积还要小,就可以剪枝了,而做最小的蛋糕的体积可以用倒推法,做第m层半径为1,高也为1,那么体积为1,做第m-1层半径为2,高也为2,那么体积为8……做第i+1层就半径为m-i,高也为m-i,那么体积为(m-i)^3,因此,从1循环到m-i把体积相加,再与现在的体积比较就行了。

3:若剩余的体积比做最大的蛋糕的体积还要大,那也可以剪枝了,方法与2相同。2与3叫做可行性剪枝。

然而,这道题却是在逗我们……

你做着做着会发现,剪来剪去反而一个不好就把最佳答案给剪掉了,程序也长,最终迎来的还是红色的WA,可是,数据告诉我们,只要确定好s、h可能的值,那么只要用到最优化剪枝,便可以AC了……(经本人亲身实验,搞了一上午的剪枝,每一次都把方案给剪了,最终改了一下循环的初值和终值,去掉可行性剪枝都能轻松AC……加上可行性剪枝因为多了判断的时间反而耗时多了一点点……)这里我将两种程序都贴出来,希望哪位大神看到了能够告诉我我哪里剪错了……不胜感激。

【代码实现】

 1 uses math;
 2 var r,h,s,v,ans,n,m,i:longint;
 3 procedure search(i,r,h,s,v:longint);
 4 var a,b,c,d:longint;
 5 begin
 6  if i=m then
 7   begin
 8    if v=0 then
 9     if s<ans then
10      ans:=s;
11    exit;
12   end;
13  if s+2*v div r>ans then
14   exit;//最优化剪枝
15  for a:=r-1 downto m-i do//注意循环变量,自己去算一算,是可以到m-i的,而不是到i
16   for b:=min(v div (a*a),h-1) downto m-i do
17    begin
18     c:=s+2*a*b;
19     d:=v-a*a*b;
20     search(i+1,a,b,c,d);
21    end;
22 end;
23 begin
24  readln(n);
25  readln(m);
26  s:=0;
27  ans:=maxlongint;
28  for r:=m to trunc(sqrt(n)) do
29   for h:=n div (r*r) downto m do
30    begin
31     s:=2*r*h+r*r;
32     v:=n-r*r*h;
33     search(1,r,h,s,v);
34    end;
35  if ans=maxlongint then
36   ans:=0;
37  writeln(ans);
38 end.
 1 uses math;
 2 var r,h,s,v,ans,n,m:longint;
 3 procedure search(i,r,h,s,v:longint);
 4 var a,b,c,d,mini,k:longint;
 5 begin
 6  mini:=0;
 7  if s+((2*v) div r)>ans then
 8   exit;//剪枝1
 9  for k:=1 to m-i do
10   mini:=mini+k*k*k;
11  if v<mini then
12   exit;//剪枝2
13  mini:=0;
14  for k:=i+1 to m do
15   mini:=mini+sqr(r-k+i)*(h-k+i);
16  if v>mini then
17   exit;//剪枝3
18  if (i=m)and(v=0) then
19   if s<ans then
20    begin
21     ans:=s;
22     exit;
23    end;
24  for a:=r-1 downto i do
25   for b:=min(v div (a*a),h-1) downto i do
26    begin
27     c:=s+2*a*b;
28     d:=v-a*a*b;
29     search(i+1,a,b,c,d);
30    end;
31 end;
32 begin
33  readln(n);
34  readln(m);
35  ans:=maxlongint;
36  for r:=m to trunc(sqrt(n)) do
37   for h:=n div (r*r) downto m do
38    begin
39     s:=2*r*h+r*r;
40     v:=n-r*r*h;
41     search(1,r,h,s,v);
42    end;
43  if ans=maxlongint then
44   ans:=0;
45  writeln(ans);
46 end.
时间: 2024-10-13 03:06:35

生日蛋糕 (codevs 1710) 题解的相关文章

code1052 地鼠游戏

贪心算法,从后往前 来自codevs的题解: 我的纠结思考过程:如果每一秒都没有重复的地鼠出现 那么肯定是一个一个挨着打如果有重复的地鼠 那么要考虑打那个更优 当然是选分值最大的 单纯这样想很合理 但是忽略了一种情况 分值小的地鼠早出现了 而后面重复的地鼠都比这个早出现的地鼠分值大我们就不能去打这只小的 而是把时间去用在打后面重复的地鼠身上如 1 2 2    5 6 7 那么按照地鼠的分值排序 然后挨着打怎么样也不对 因为分值大的地鼠也有可能停留时间长 我们早打了他 会导致时间短的地鼠没法打

code1006 等差数列

我绞尽脑汁想一个更好的算法,然而不能如愿,只好写一个n^3的了 很简单,就是暴力搜索(还好n<100) 先排序,然后循环i=1ton,j=i+1ton 把a[i]a[j]确定为等差数列开始的两个数,确定公差,然后用search()搜这个数列的长度 取所有的最大值即可 代码如下: #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algo

trie tree

trie树也叫字典树,前缀树 字典树(Trie)有如下几条性质 结点不存值,依靠树枝(边)存值 从根节点到某一处标记点为一个单词 每个结点到其子节点的边上的值各不相同 插入和查询复杂度均为O(mn),m为字符串个数,n为字符串平均长度 树深度由最长字符串决定 依次便可做出trie树的结点结构 struct trie{ int cnt=0; bool word=0; trie * son[26]={0}; }; 让我们依次价绍这几个成员的含义 cnt 这条边上有几个单词经过,即有多少指定的前缀相同

提高组刷题营 DAY 1 下午

DFS 深度优先搜索 通过搜索得到一棵树形图 策略:只要能发现没走过的点,就走到它.有多个点可走就随便挑一个,如果无路可走就回退,再看有没有没走过的点可走. 在图上寻找路径[少数可用最短路解决]:最短路不能解决路径有顺序的,也就是如果路径的边权与之前经过的点火这路有关,那就只能深搜 解决递归形式的问题 有后效性的选择问题 组合问题 状态可能很多,因此数据范围一般较小 1.状态表示 2.剪枝 剪枝的方法: 最优答案剪枝 记忆化剪枝 可行性剪枝 …… 1.洪水[ 1s 32M ] 题解 数据范围小,

codevs 1512 转向游戏 题解

Codevs 1512转向游戏 题解 时间限制: 1 s 空间限制: 1000 KB 题目等级 : 白银 Silver 题解 题目描述 Description 小明自认为方向感很好,请小红来测试.小红先让小明面对北方立正站好,然后发出"向左转""向右转"或"向后转"的命令.每个命令执行后,小明都正确地说出了他面对的方向.小红的命令共N个(1≤n≤10000),请你统计小明说[南]的次数. 命令是以数字方式表达: 0---向左转 1---向右转 2

codevs 1506 传话 题解

Codevs 1506传话 题解 1506 传话--这个题目的解法很多,你能想到几种? 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 白银 Silver 题解 题目描述 Description 一个朋友网络,如果a认识b,那么如果a第一次收到某个消息,那么会把这个消息传给b,以及所有a认识的人. 如果a认识b,b不一定认识a. 所有人从1到n编号,给出所有"认识"关系,问如果i发布一条新消息,那么会不会经过若干次传话后,这个消息传回给了i,1<=i<=n

CODEVS 1048 石子归并 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://codevs.cn/problem/1048/ 题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1].问安排怎样的合并顺序,能够使得总合并代价达到最小. 输入描述 Input Description 第一行一个整数n(n<=100) 第二行n个整数w1,w2...wn  (wi <=

CODEVS 1081 线段树练习 2 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://codevs.cn/problem/1081/ 题目描述 Description 给你N个数,有两种操作 1:给区间[a,b]的所有数都增加X 2:询问第i个数是什么? 输入描述 Input Description 第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,表示操作的个数. 接下来Q行每行若干个整数.如果第一个数是1,后接3个正整数a,b,X,表示在区间[a,b]内每个数增加X,如果

CODEVS 1080 线段树练习 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://codevs.cn/problem/1080/ 题目描述 Description 一行N个方格,开始每个格子里都有一个整数.现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[a,b]中所有元素的和:修改的规则是指定某一个格子x,加上或者减去一个特定的值A.现在要求你能对每个提问作出正确的回答.1≤N<100000,,提问和修改的总数m<10000条. 输入描述 Input Descrip