1. 程式人生 > >【NOIP2018 信心賽】滋潤

【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;
}