• 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
    ",ans);
    63     return 0;
    64 }
    View Code
  • 相关阅读:
    免费的编程中文书籍索引
    整理书签博客和文章
    【De8ug玩docker】-Docker常用命令操作
    Linux中的那些英文缩写和她的含义们
    Docker和DevOps是找工作必备技能
    【De8ug玩docker】-命令行只显示-bash-4.1#
    应该知道的Linux技巧
    阿里云服务器linux(centos)常用命令
    jQuery运维开发之第十七天
    js+dom开发第十六天
  • 原文地址:https://www.cnblogs.com/liutianrui/p/7536625.html
Copyright © 2020-2023  润新知