bzoj 1082: [SCOI2005]栅栏 题解

1082: [SCOI2005]栅栏

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 2340  Solved: 991
[Submit][Status][Discuss]

Description

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

Input

  第一行为整数m(m<= 50)表示木材店老板可以提供多少块木材给约翰。紧跟着m行为老板提供的每一块木板的长
度。接下来一行(即第m+2行)为整数n(n <= 1000),表示约翰需要多少木材。接下来n行表示他所需要的每一块木板
的长度。木材的规格小于32767。(对于店老板提供的和约翰需要的每块木板,你只能使用一次)。

Output

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

Sample Input

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

Sample Output

7

HINT

25切出 21 30切出 20 40切出 19、18 50切出 15、16、17

 

  这道题正解是二分答案+爆搜,我们可以明确一些事情,如果存在解为x那么我满足的木板的解一定有一个是从小向大排列前x个,那么我们可以对它进行二分答案。那么check就需要我们DFS了,为了剪枝,我们将所需要的木板从大向小枚举而将老板的木板从小向大枚举,如果一个老板卖的木板被我们用的小于最小的需要的木板,就将它现在的长度加入waste,代表我们用剩下的无法被利用的木料的长度,如果tot(所有老板卖的木料的总长度)-waste<sum[mid](需要的木板的前缀和)我们就直接return就好了。

  最后还有一个剪枝,如果前后两块挨着的木板长度一致,那么我们下一次搜索的起始点就一定不需要比这个点靠前,因为他们是等价的。

  这道题给了我们一些启示:

    1.搜索不一定都是那么的裸,我们还应通过他答案的性质与其他算法有机结合一下。

    2.对于一些等价搜索,我们可以利用之前的成果进行剪枝,可能会有意想不到的结果。

 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 #include<cmath>
 8 #include<map>
 9 #define N 1055
10 using namespace std;
11 int n,m,c[N];
12 int b[N],a[N],sum[N],tot;
13 bool dfs(int k,int waste,int wz,int mid)
14 {
15     if(k==0)return 1;
16     if(sum[mid]>tot-waste)return 0;
17     int t=waste;
18     for(int i=wz;i<=n;i++)
19     {
20         if(c[i]>=a[k])
21         {
22             t=waste;
23             c[i]-=a[k];
24             if(c[i]<a[1])
25                 t+=c[i];
26             if(a[k-1]==a[k])
27             {
28                 if(dfs(k-1,t,i,mid))
29                     return 1;
30             }
31             else if(dfs(k-1,t,1,mid))return 1;
32             c[i]+=a[k];
33         }
34     }
35     return 0;
36 }
37 int main()
38 {
39     scanf("%d",&n);
40     for(int i=1;i<=n;i++)
41     {
42         scanf("%d",&b[i]);
43         tot+=b[i];
44     }
45     scanf("%d",&m);
46     for(int i=1;i<=m;i++)
47         scanf("%d",&a[i]);
48     sort(b+1,b+n+1);
49     sort(a+1,a+m+1);
50     while(a[m]>b[n]) m--;
51     for(int i=1;i<=m;i++)
52         sum[i]+=sum[i-1]+a[i];
53     int li=0,ri=m;
54     int ans;
55     while(li<=ri)
56     {
57         memcpy(c,b,sizeof(b));
58         int mid=(li+ri)/2;
59         if(dfs(mid,0,1,mid))li=mid+1,ans=mid;
60         else ri=mid-1;
61     }
62     printf("%d\n",ans);
63     return 0;
64 }
View Code
posted @ 2017-09-17 18:56  Hzoi_joker  阅读(398)  评论(0编辑  收藏  举报