题目描述 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();
}