设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
仅一行,是一个正整数S(若无解则S = 0)。
*剪枝:明确每层r和h的最大最小值。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define inf 1000000009
using namespace std;
int n,m,ans=inf,r1,minv[23];
int get(){
char c;while(!isdigit(c=getchar()));
int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
return v;
}
int maxv(int x,int r,int h){
int tot=0;
while(x){
--r,--h;
tot+=r*r*h;
--x;
}
return tot;
}
void dfs(int x,int sum,int lr,int lh,int v){
if(sum>=ans)return;
if(x==0 && v==n){ans=min(ans,sum);return;}
if(((v+maxv(x,lr,lh))<n)||((v+minv[x])>n))return;//用剩下的最小值和最大值来剪枝;
int tmpv=(n-v-minv[x-1])/x;
for(int r=x;r<=min(lr-1,(int)sqrt(tmpv));++r)
for(int h=x;h<=min(lh-1,(tmpv/x));++h)
dfs(x-1,sum+2*h*r,r,h,v+r*r*h);
}
int main(){
n=get(),m=get();
for(int i=1;i<=m;++i)minv[i]=i*i*i+minv[i-1];
int tmpv=(n-minv[m-1])/m;
for(int r=m;r*r<=tmpv;++r)
for(int h=m;h*r<=tmpv;++h)
dfs(m-1,r*r+2*h*r,r,h,r*r*h);
if(ans!=inf)printf("%d\n",ans);
else printf("0\n");
return 0;
}
描述
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define inf 100000008
using namespace std;
int n,now,a[101];
bool cmp(const int &aa,const int &bb){return(aa>bb);}
bool dfs(int t,int s){
if(!s && t)s=now;
if(!t){
if(s)return 0;
else return 1;
}
bool flag=0;
for(int i=1;i<=n;++i){
if(a[i]==a[i-1])continue;
if(a[i]<=s){
int tmp=a[i];
a[i]=inf;
flag|=dfs(t-1,s-tmp);
a[i]=tmp;
if(flag)return flag;
if(s==now || a[i]==s)return 0;//存在不能复原的木棍
}
}
return flag;
}
int main(){
while(scanf("%d",&n)==1){
if(n==0)break;int sum=0;
for(int i=1;i<=n;++i)scanf("%d",&a[i]),sum+=a[i];
sort(a+1,a+1+n,cmp);
for(int i=a[1];i<=sum;++i)if(sum%i==0){
now=i;
if(dfs(n,i))break;
}
printf("%d\n",now);
}
return 0;
}
*剪枝:存在不能复原的木棍即退出
Fence Rails 栅栏的木料(fence8)
题目描述
农民John准备建一个栅栏来围住他的牧场。他已经确定了栅栏的形状,但是他在木料方面有些问题。 当地的杂货储存商扔给John一些木板,而John必须从这些木板中找出尽可能多所需的木料。 当然,John可以切木板。因此,一个9英尺的木板可以切成一个5英尺和一个4英尺的木料 (当然也能 切成3个3英尺的,等等)。John有一把梦幻之锯,因此他在切木料时,不会有木料的损失。 所需要的木料规格都已经给定。你不必切出更多木料,那没有用。
输入
第1行: N (1 <= N <= 50), 表示提供的木板的数目 第2行到第N+1行: N行,每行包括一个整数,表示各个木板的长度。 第N+2行: R (1 <= R <= 1023), 所需木料的数目 第N+3行到第N+R+1行: R行,每行包括一个整数(1 <= ri <= 128)表示所需木料的长度。
输出
只有一行,一个数字,表示能切除的最多的所需木料的树木。当然,并不是任何时候都能切出所有所需木料。
样例输入
4
30
40
50
25
10
15
16
17
18
19
20
21
25
24
30
样例输出
7
【思路】直接dfs会T。二分+剪枝。显然先切小木料得到的数量最多,将木板和木料从小到大排序,二分寻找前i个木料是否可行。
剪枝:若不可用的废料大于(木板总长-前i个木料总长),那么肯定不够切,无解,剪枝。
【注意】二分中l与r的赋值,及结果的输出。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define maxn 1333
using namespace std;
int n,r,a[maxn],l[maxn],ans=0,suma,sumb[maxn],b[maxn],t;
bool vst[maxn];
int get(){
char c;while(!isdigit(c=getchar()));
int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
return v;
}
void init(){
n=get();
for(int i=1;i<=n;++i)a[i]=get(),suma+=a[i];
t=r=get();
for(int i=1;i<=r;++i)b[i]=get();
sort(a+1,a+1+n);
sort(b+1,b+1+r);
for(int i=1;i<=r;++i){
sumb[i]=sumb[i-1]+b[i];
if(sumb[i]>suma && t==r)t=i-1;
}
}
bool okit(int x,int lst,int res,int tot){
if (x==0)return 1;
if(res>tot)return 0;
if(b[x]!=b[x+1])lst=n;
for(int i=lst;i>=1;--i){
if(a[i]>=sumb[x])return 1;
if(a[i]>=b[x]){
a[i]-=b[x];
if(okit(x-1,i,res+(a[i]<b[1]?a[i]:0),tot)){a[i]+=b[x];return 1;}
a[i]+=b[x];
}
}
return 0;
}
int two(int L,int R){
int mid;
while(L<=R){
mid=(L+R)>>1;
if(okit(mid,n,0,suma-sumb[mid]))L=mid+1;
else R=mid-1;
}
return (L-1);
}
int main(){
init();
printf("%d\n",two(1,t));
return 0;
}