把原木和新木快排一遍,每次二分枚举可以切的木块数,再去验证,这应该是一个比较好想代码又好写的思路。
验证时,设枚举的木块数为k。由贪心思想可知,我们挑前k小的新木来切割
一个特别关键的剪枝:统计切剩下的board(无法再切下rail)总和,这个值大于board总和减去rail总和,无解。
这个代码里写有解释,应该是很清楚的
/*
ID:xsy97051
LANG:C++
PROG:fence8
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int a[51],b[1024],b_sum[1024];
int n,m,ans,sum;
//验证时,设枚举的木块数为k。由贪心思想可知,我们挑前k小的新木来切割。
bool check(int num,int now,int q,int limit)
{
if(num==0) return 1;
//统计切剩下的board(无法再切下rail)总和,这个值大于board总和减去rail总和,无解(此剪枝最关键)
if(q>limit) return 0;
if(b[num]!=b[num+1]) now=n;
//事实证明,每次先用大的原木切割会更快,因为相对来说,它能分的个数更多
for(int i=now;i>0;i--)
{
if(a[i]>=b_sum[num])
return 1;
if(a[i]>=b[num])
{
a[i]-=b[num];
if(check(num-1,i,q+(a[i]<b[1])?a[i]:0,limit))
{
a[i]+=b[num];
return 1;
}
a[i]+=b[num];
}
}
return 0;
}
//把原木和新木快排一遍,每次二分枚举可以切的木块数,再去验证。
int erfen(int l,int r)
{
if(l==r) return l;
int mid=(l+r)/2+1;
if(check(mid,n,0,sum-b_sum[mid]))
return erfen(mid,r);
else
return erfen(l,mid-1);
}
int main()
{
freopen("fence8.in","r",stdin);
freopen("fence8.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
int tep=sum;
cin>>m;
for (int i=1;i<=m;i++)
cin>>b[i];
sort(a+1,a+n+1);
sort(b+1,b+m+1);
for(int i=1;i<=m;i++)
b_sum[i]=b_sum[i-1]+b[i];
int flag=m;
for(int i=1;i<=m;i++)
{
tep-=b[i];
if(tep<0)
{
flag=i;
break;
}
}
cout<<erfen(0,flag)<<endl;
return 0;
}