zoukankan      html  css  js  c++  java
  • 【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 }
  • 相关阅读:
    16. 3Sum Closest
    17. Letter Combinations of a Phone Number
    20. Valid Parentheses
    77. Combinations
    80. Remove Duplicates from Sorted Array II
    82. Remove Duplicates from Sorted List II
    88. Merge Sorted Array
    257. Binary Tree Paths
    225. Implement Stack using Queues
    113. Path Sum II
  • 原文地址:https://www.cnblogs.com/rabbit1103/p/9807175.html
Copyright © 2011-2022 走看看