【NOIP2018 信心赛】滋润

【问题描述】

饿了么?万科憋?香不香? 最喜欢去不远处的van♂ke吃 最喜欢的脾肾嗑了。 勤俭持家,他总是打包剩
菜。 饭桌上可以看成有 个剩菜, 个打包盒,其中每个剩菜都有体积 ,每个打包盒都有容量 。为了环保
想知道最少要用几个打包盒装下所有的剩菜?注意, 认为剩菜不可分割,但是他并不介意把两份或以上的菜装
在一个打包盒里。若无论如何都装不下请输出-1

【输入格式】

输入文件为 输入数据的第一行为两个正整数 和 ,意义如题所示 接下来一行 个非负整数,第i个
表示 ,意义如题所示 接下来一行 个非负整数,第i个表示 ,意义如题所示

【输出格式】

输出文件为 输出文件包含一行,表示最少的打包盒数量

【输入输出样例】

见下发文件 和

【样例解释】

用11的包装下3,用18的包装下10、4和2,共2个包 当然也存在别的只用2个包的装法

思路

考虑状压dp。设f[x]表示选取物品状态为x的最小背包数量,g[x]为选取物品状态为x,背包数量为f[x]时最后一个包剩余的空间,枚举x中1的位然后转移就行了。考虑这样做的正确性,我们一定枚举出了所有可能的放置顺序。这样有80分
可以发现复杂度瓶颈在于枚举x中不为1的位,这个用树状数组时调用的lowbit就可以优化到n2 。可以得到100分的高分

代码

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define drp(i,st,ed) for (register int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
#define lowbit(x) ((x)&(-(x)))
 
const int INF=10777216;
const int N=16778216;
 
int a[N],b[505],f[N],g[N],n,m;
 
int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}
 
bool cmp(int x,int y) {
    return x>y;
}
 
int main(void) {
    freopen("wilgotne.in","r",stdin);
    freopen("wilgotne.out","w",stdout);
    n=read(),m=read(); int lim=(1<<n)-1;
    rep(i,1,n) a[i]=read();
    drp(i,n,1) a[1<<(i-1)]=a[i];
    rep(i,1,m) b[i]=read();
    std:: sort(b+1,b+m+1,cmp);
    rep(i,1,lim) {
        f[i]=INF;
        for (int x=i;x;x-=lowbit(x)) {
            int tmp=lowbit(x);
            if (g[i-tmp]>=a[tmp]&&(f[i-tmp]<f[i]||(f[i-tmp]==f[i]&&g[i-tmp]-a[tmp]>g[i]))) {
                f[i]=f[i-tmp];
                g[i]=g[i-tmp]-a[tmp];
            } else if (b[f[i-tmp]+1]>=a[tmp]&&(f[i-tmp]+1<f[i]||(f[i-tmp]+1==f[i]&&b[f[i-tmp]+1]-a[tmp]>g[i]))) {
                f[i]=f[i-tmp]+1;
                g[i]=b[f[i]]-a[tmp];
            }
        }
    }
    if (f[lim]>m) puts("-1");
    else printf("%d\n", f[lim]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值