搜索

描述
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外,以上所有数据皆为正整数)
输入
有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。
输出

仅一行,是一个正整数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;
}






描述
乔治拿来一组等长的木棒,将它们随机地裁断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。
输入
输入包含多组数据,每组数据包括两行。第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。
输出
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。

#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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值