首先贪心的性质是显然的,我们如果要取k个一定要取最小的k个,且尽量用最小的木材满足最大的需求,以减少浪费。
既然所取的k个是单调的,那么可以考虑二分,贪心的判断是否可行。
剪枝:
如果当前用了的+剩下所需最少的(即维护b[i]的前缀和)>所有的a。那么不可能合法,剪掉。
qwq。。只想到这一个剪枝,题解还给提供了另一个,我感觉应该用处不大吧。。。就是如果下一个和这一个相同,那么可以不从第一块木材开始搜。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,ans,rest,s;
int a[55],c[55],b[1005],s0[1005];
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
bool dfs(int now,int last)
{
if (now<=0) return 1;
if (rest+s0[now]>s) return 0;
for (int i=last;i<=m;i++)
if (c[i]>=b[now])
{
c[i]-=b[now];
if (c[i]<b[1]) rest+=c[i];
if (b[now]==b[now-1])
{
if (dfs(now-1,i)) return 1;
}
else
{
if (dfs(now-1,1)) return 1;
}
if (c[i]<b[1]) rest-=c[i];
c[i]+=b[now];
}
return 0;
}
inline bool judge(int mid)
{
memcpy(c,a,sizeof(a));
rest=0;
return dfs(mid,1);
}
int main()
{
m=read();
for (int i=1;i<=m;i++) a[i]=read(),s+=a[i];
n=read();
for (int i=1;i<=n;i++) b[i]=read();
sort(a+1,a+m+1); sort(b+1,b+n+1);
for (int i=1;i<=n;i++) s0[i]=s0[i-1]+b[i];
while (s0[n]>s) n--;
int l=0,r=n;
while (l<=r)
{
int mid=l+r>>1;
if (judge(mid)) ans=mid,l=mid+1; else r=mid-1;
}
cout << ans;
return 0;
}