【bzoj1082】【SCOI2005】栅栏

问题描述

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

你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰最多能够得到多少他所需要的木板。

输入格式

第一行为整数m(m<= 50)表示木材店老板可以提供多少块木材给约翰。紧跟着m行为老板提供的每一块木板的长度。

接下来一行(即第m+2行)为整数n(n <= 1000),表示约翰需要多少木材。

接下来n行表示他所需要的每一块木板的长度。木材的规格小于32767。(对于店老板提供的和约翰需要的每块木板,你只能使用一次)。

输出格式

只有一行,为约翰最多能够得到的符合条件的木板的个数。

样例输入

4
30
40
50
25
10
15
16
17
18
19
20
21
25
24
30

样例输出

7

题解

如果约翰能够得到tot个木板,那么对于任意k<=tot,约翰一定能得到k个木板,答案具有单调性,可以二分了。

二分一个答案mid,用dfs检验

 

然后开始剪枝

显然,对于一块木材,如果不能满足当前木板,也一定不能满足比当前木板长的木板。我们把木材和木板按长度从小到大排序,这样当木材不能满足当前木板时,后面的木板都可以剪掉了。

考虑二分的范围。如果所需木板的总长度比木材的总长度长,显然无论如何不可能满足,直接把最长的木板删掉,直到木板的总长度<=木材的总长度。

对于一块木材,如果不能满足当前木板,也一定不能满足比当前木板长的木板,所以,如果约翰可以得到mid个木板,那么一定是前mid个木板。倒过来从第mid个木板到第1个木板锯,小的木材锯出大的木板,更容易剪枝。

res表示目前锯剩下的不能再锯出需要的木板的木材总长度(即浪费的木材长度),s[]表示木板长度的前缀和,sum表示木材的总长度,如果res+s[mid]>sum,接下来再怎么锯都不能锯出mid个木板,剪枝。

对于res的转移,如果当前木材的剩余长度比所需木板的最小长度还短,显然不可能再锯出木板,res+剩余长度;否则,还有可能锯出木板,res不变。

设las表示上一块锯过的木材,如果当前木板长度和下一个木板一样,下一个从第las块木材开始锯(因为当前木板已经试出las前的木材都锯不出来),否则从第1块木材开始锯。

 

 

 1 #include <algorithm>
 2 #include <cstring>
 3 #include <cstdio>
 4 int m,n,a[1005],b[1005],c[1005],mid,ans,sum,s[1005];
 5 bool vis[1005];
 6 bool dfs(int res,int las,int t)  // 多余长度,提供的上一个木板编号,需要的第t个木板 
 7 {
 8     int i,j,k;
 9     if (!t) return 1;
10     if (res+s[mid]>sum) return 0;
11     for (i=las;i<=m;i++)
12       if (b[i]>=c[t])
13       {
14             b[i]-=c[t];
15             if (dfs(res+(b[i]<c[1]?b[i]:0),c[t]==c[t-1]?i:1,t-1)) return 1;
16             b[i]+=c[t];
17       } 
18     return 0;
19 }
20 int main()
21 {
22     int i,j,k,l=0,r;
23     scanf("%d",&m);
24     for (i=1;i<=m;i++)
25       scanf("%d",&a[i]),
26       sum+=a[i];
27     scanf("%d",&n);
28     for (i=1;i<=n;i++)
29       scanf("%d",&c[i]);
30     std::sort(a+1,a+m+1);
31     std::sort(c+1,c+n+1);
32     for (i=1;i<=n;i++)
33       s[i]=s[i-1]+c[i];
34     while (sum<s[n]) n--;
35     r=n;
36     while (l<=r)
37     {
38         mid=(l+r)>>1;
39         memcpy(b,a,sizeof(b));
40         if (dfs(0,1,mid)) ans=mid,l=mid+1;
41         else r=mid-1;
42     }
43     printf("%d",ans);
44     return 0;    
45 }

 

posted @ 2018-10-17 21:20  SAKURA12  阅读(148)  评论(0编辑  收藏  举报