1082: [SCOI2005]栅栏 二分+dfs+剪枝

首先贪心的性质是显然的,我们如果要取k个一定要取最小的k个,且尽量用最小的木材满足最大的需求,以减少浪费。
既然所取的k个是单调的,那么可以考虑二分,贪心的判断是否可行。
剪枝:
如果当前用了的+剩下所需最少的(即维护b[i]的前缀和)>所有的a。那么不可能合法,剪掉。


qwq。。只想到这一个剪枝,题解还给提供了另一个,我感觉应该用处不大吧。。。就是如果下一个和这一个相同,那么可以不从第一块木材开始搜。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,ans,rest,s;
int a[55],c[55],b[1005],s0[1005];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}   
bool dfs(int now,int last)
{
    if (now<=0) return 1;
    if (rest+s0[now]>s) return 0;
    for (int i=last;i<=m;i++) 
        if (c[i]>=b[now])
        {
            c[i]-=b[now];
            if (c[i]<b[1]) rest+=c[i];
            if (b[now]==b[now-1]) 
            {
                if (dfs(now-1,i)) return 1;
            }
            else
            {
                if (dfs(now-1,1)) return 1;
            }
            if (c[i]<b[1]) rest-=c[i];
            c[i]+=b[now];
        }
    return 0;
}
inline bool judge(int mid)
{
    memcpy(c,a,sizeof(a));
    rest=0;
    return dfs(mid,1);
}
int main()
{
    m=read();
    for (int i=1;i<=m;i++) a[i]=read(),s+=a[i];
    n=read();
    for (int i=1;i<=n;i++) b[i]=read();
    sort(a+1,a+m+1); sort(b+1,b+n+1);
    for (int i=1;i<=n;i++) s0[i]=s0[i-1]+b[i];
    while (s0[n]>s) n--;
    int l=0,r=n;
    while (l<=r)
    {
        int mid=l+r>>1;
        if (judge(mid)) ans=mid,l=mid+1; else r=mid-1;
    }
    cout << ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值