bzoj 1082 [SCOI2005]栅栏 搜索姿势不对毁一生

17 篇文章 0 订阅

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

二分+判断可行性问题越来越多了~

注意二分 的 单调 。。比如 有限制的最多

如你所见有点像 poj 1011 sticks。。

但是呢 这个 问最多能 搞出来多少 块木板。 那怎么办?

那么我们用二分来解决 看能够拼成多少。

然后 比较关键的是剪枝,因为它会影响搜索的姿势。。

先说 大概框架 前k个木板枚举被那个木材提供。也就是 没回dfs的时候 循环m

那么我们发现 数据中存在 相等的木棍话的话,循环是不必从1开始的 而是可以从 当前被配到的 开始的

所以对木板搜索是从大往小搜

然后 如果一个木材 被裁 之后剩下的 连最小的木材 都拼不成了 记为 waste,
如果 waste+get>sum 就剪枝

事实证明 搜索的顺序 很关键

这里写图片描述

第一个AC 是 吧木板从大到小搜,木材从小到大

然后T的是 都从小到大

最后一个A的是 都从大到小

解释一下原因

最小的 拼起来较为灵活 所以放到最后

就比如

这里写图片描述

顺序关系就很明显了吧。

但是为什么 都从大到小和 一个从大到下一个从小到大有 不同 很玄学【我不知道】

#include<cstdio>
#include<algorithm>
#include<cstring>
//by mars_ch
using namespace std;
int m;
int a[55];
int n,need,tot;
int b[1005],sum[1005],res; 
bool dfs(int x,int num,int waste)
{
    bool res=false;
    if(waste+need>tot) return false;
    for(int i=num;i<=m;i++)
    {
        if(a[i]<b[x]) continue;
        a[i]-=b[x];
        if(a[i]<b[1])waste+=a[i];
        if(x == 1) res=true;
        else if(b[x] == b[x-1]) res=dfs(x-1,i,waste);
        else res=dfs(x-1,1,waste);

        waste-=a[i];
        a[i]+=b[x];
        if(res) return true;
    }
    return false;

}
bool check(int x)
{
    need=sum[x];
    if(dfs(x,1,0)) return true;
    return false;
}
int main()
{
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&a[i]);
        tot+=a[i];
    }
    sort(a+1,a+m+1);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
    }
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]+b[i];
    }
    int l=0,r=n,ans=0;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(check(mid))
        {
            l=mid+1;
            ans=mid;
         } 
         else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值