农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材。于是农夫约翰到木材店购
买木材。可是木材店老板说他这里只剩下少部分大规格的木板了。不过约翰可以购买这些木板,然后切割成他所需
要的规格。而且约翰有一把神奇的锯子,用它来锯木板,不会产生任何损失,也就是说长度为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);
}