• 【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 }
  • 相关阅读:
    JS魔法堂:阻止元素被选中
    JS魔法堂之实战:纯前端的图片预览
    CentOS6.5菜鸟之旅:纯转载Linux目录结构
    Vim杂记:Sublime的配色方案
    Vim杂记:markdown插件
    CentOS6.5菜鸟之旅:中文编辑器忍痛放弃Sublime
    JS魔法堂:Data URI Scheme介绍
    CentOS6.5菜鸟之旅:安装ATI显卡驱动
    JS魔法堂:获取当前脚本文件的绝对路径
    腊八蒜
  • 原文地址:https://www.cnblogs.com/rabbit1103/p/9807175.html
Copyright © 2020-2023  润新知