栅栏的木料(二分+dfs)

41 篇文章 0 订阅
题目描述 Description

农民John准备建一个栅栏来围住他的牧场。他已经确定了栅栏的形状,但是他在木料方面有些问题。当地的杂货储存商扔给John一些木板,而John必须从这些木板中找出尽可能多所需的木料。

当然,John可以切木板。因此,一个9英尺的木板可以切成一个5英尺和一个4英尺的木料 (当然也能切成3个3英尺的,等等)。John有一把(完美的)梦之锯,因此他在切木料时,不会有木料的损失。

所需要的栅栏长度可能会有重复(比如,一个3英尺和另一个3英尺长的栅栏可能同时都需要)。所需要的木料规格都已经给定。你不必切出更多木料,那没有用。

输入描述 Input Description

第1行: N (1 <= N <= 50), 表示提供的木板的数目

第2行到第N+1行: N行,每行包括一个整数,表示各个木板的长度。

第N+2行: R (1 <= R <= 1023), 所需木料的数目

第N+3行到第N+R+1行: R行,每行包括一个整数(1 <= ri <= 128)表示所需木料的长度。

输出描述 Output Description

只有一行,一个数字,表示能切出的最多的所需木料的数目。当然,并不是任何时候都能切出所有所需木料。

样例输入 Sample Input

4
30
40
50
25
10
15
16
17
18
19
20
21
25
24
30

 

样例输出 Sample Output

7

 
  

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
int len[53],ans[1024],lentot,n,m,sum[1024],i,j,ok,wanow,wamax,tt,head,tail,mid;  
int cmp(int a,int b)  
{  
if (a>b)  
return 1;  
else  
return 0;  
}  
void dfs(int k,int last)//k表示当前该截那一段了,从需求长的开始截  
  
{   
	int i,where;  
	if (ans[k]==ans[k+1])//对于相同长度的需求可从上一层的位置向下搜索,减小搜索范围  
	where=last;   
	else  
	where=1;  
	for (i=where;i<=n;++i)//木板长度从大到小开始搜索  
	 if (len[i]>=ans[k])//可以截出当前需求  
	   {  
	    len[i]-=ans[k];  
	    if (len[i]<ans[1])  
	     wanow+=len[i];//当前浪费的长度增加  
	    if (k==1)//可以满足条件  
	     ok=1;  
	    else  
	     if (k>1&&wanow<=wamax)  
	        dfs(k-1,i);  
	    if (len[i]<ans[1])  
	     wanow-=len[i];  
	    len[i]+=ans[k];  
	    if (ok)  
	     return ;  
	   }   
}  
void find()  
  
{  
	head=0;  
	while (head<m)   
	 if (len[head+1]>ans[head+1])//保证一块木板能截出一段所需木料的长度,计算至少能满足多少需求   
	   head++;   
	 else    
	   break;    
	tail=m; 	  
	while (sum[tail]>lentot)//所需木料总长超过木板的总长,舍去最长的需求   
	 tail--;    
	while (ans[tail]>len[1])//最长的所需木料超过最长的木板的长度,无法满足舍去   
	 tail--;  
	while (tail>head)//二分寻找能满足的所需木料数量   
	{    
	mid=(head+tail+1)>>1;    
	wanow=0;   
	for (i=n;i>=1;i--)   
	 if (len[i]<ans[1])  
	   wanow+=len[i];//最短的木板无法满足最小的需求,木板就直接浪费了,计算直接废弃了多少  
	wamax=lentot-sum[mid];//最多可以浪费多少(极限值)  
	ok=0;    
	if (wanow<=wamax)//如果直接浪费的已经超过极限,那就没必要再判断是否可以满足需求了  
	 dfs(mid,1);  
	if (ok)    
	head=mid;//head向后移,继续判断是否还能满足更多的需求  
	else  
	tail=tail-1;//当前的需求无法满足,再去掉一个长度长的木板  
	}  
	printf("%d",head);  
}  
int main()  
{  
    freopen("a.in","r",stdin);
	scanf("%d",&n);  
	for (i=1;i<=n;i++)  
	 {  
	  scanf("%d",&len[i]);  
	  lentot+=len[i];//计算木板的总长度  
	 }   
	scanf("%d",&m);  
	for (i=1;i<=m;i++)  
	 scanf("%d",&ans[i]);//读入所有需求   
	sort(ans+1,ans+m+1);//将需求从小到大排序,因为需求越短越容易满足   
	sort(len+1,len+n+1,cmp);//将木板长度从大到小排序,越长的木板越容易满足栅栏的需求  
	for (i=1;i<=m;i++)  sum[i]=sum[i-1]+ans[i];//计算从最小的需求到当前的需求最少需要多少木料   
	find();  
}  


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值