栅栏 二分 dfs 贪心

 农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材。于是农夫约翰到木材店购
买木材。可是木材店老板说他这里只剩下少部分大规格的木板了。不过约翰可以购买这些木板,然后切割成他所需
要的规格。而且约翰有一把神奇的锯子,用它来锯木板,不会产生任何损失,也就是说长度为10的木板可以切成长
度为8和2的两个木板。你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰
最多能够得到多少他所需要的木板。

 

 

原料和需求排一下序,然后二分需求看看是否能完成

贪心思路是用最小的原料完成最大的任务,这样,剩下来的原料如果连最小的需求都无法完成,就加到rest里面,

相当于是废掉的材料。

剪枝方面,如果走到了最后一块,说明搜索完成,Return标记为一,直接返回。

如果下一块长度与当前长度相等,枚举起始位置由当前位置开始,因为前面的都小于当前原料长度,所以不用搜索,减少搜索复杂度

否则从初始位置开始搜索

 

这里的板材长度相当于visit了,因为板材切掉之后,再次枚举到和当前长度相当的时候会因为长度不够而无法取到。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;

int a[1005],b[1005],ans,rest,need,tot,n,m,sum[1005];

bool dfs(int bound,int start)
{
	if(rest+need>tot) return 0;// 非法状态 
	bool Return=0;
	for(int i=start;i<=m;i++)
	{
		if(a[i]>=b[bound])
		{
			a[i]-=b[bound];
			if(a[i]<b[1]) rest+=a[i];
			if(bound==1) Return=1;//枚举结束 
			else if(b[bound]==b[bound-1]) Return=dfs(bound-1,i);//下一个目标长度等于当前目标长度,所以枚举起始位置从此开始,因为有过升序排序,所以前面的都不符合状态 
			else Return=dfs(bound-1,1);//不符合剪枝条件,从1开始枚举 
			rest-=a[i];
			a[i]+=b[bound];
			if(Return) return 1;
		}
	}
	return 0;//无法满足枚举的目标 
}

int main()
{
	scanf("%d",&m);
	for(int i=1;i<=m;i++)scanf("%d",&a[i]);
	sort(a+1,a+1+m);
	for(int i=1;i<=m;i++) tot+=a[i];
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
	sort(b+1,b+1+n);
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+b[i];
	int l=0,r=n,mid=0;
	ans=0; 
	while(r>=l)
	{
		rest=0;
		mid=(l+r)>>1;
		need=sum[mid];
		if(dfs(mid,1))
		{
			ans=mid;
			l=mid+1;
		}
		else
		{
			r=mid-1;
		}
	}
	printf("%d\n",ans);
} 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值