1. 程式人生 > >bzoj3717 [PA2014]Pakowanie

bzoj3717 [PA2014]Pakowanie

owa for bit 否則 esp 每次 編號 從大到小 使用

Description

你有n個物品和m個包。物品有重量,且不可被分割;包也有各自的容量。要把所有物品裝入包中,至少需要幾個包?

Input

第一行兩個整數n,m(1<=n<=24,1<=m<=100),表示物品和包的數量。
第二行有n個整數a[1],a[2],…,a[n](1<=a[i]<=10^8),分別表示物品的重量。
第三行有m個整數c[1],c[2],…,c[m](1<=c[i]<=10^8),分別表示包的容量。

Output

如果能夠裝下,輸出一個整數表示最少使用包的數目。若不能全部裝下,則輸出NIE。

Sample Input

4 3
4 2 10 3
11 18 9

Sample Output

2

正解:背包+狀壓$dp$。

這道題只有$24$個物品,我們可以考慮狀壓。

還有一個很顯然的事,背包肯定是從大到小填的。

那麽我們就可以直接開始$dp$了,設$f[S]$表示當前物品集合為$S$,且當前背包所用容量為$f[S]$,$g[S]$為當前背包的編號,也就是背包數量。

那麽每次我們可以枚舉一個物品加進來,如果沒有炸掉當前背包,就可以直接填進去,否則就要開一個新背包,如果連新背包都開不下,那麽這個轉移就是不合法的。

 1 #include <bits/stdc++.h>
 2
#define il inline 3 #define RG register 4 #define ll long long 5 #define lb(x) (x & -x) 6 #define inf (2147483640) 7 #define SIZE (16777216) 8 9 using namespace std; 10 11 int f[SIZE],g[SIZE],bin[SIZE],a[110],c[110],n,m; 12 13 il int gi(){ 14 RG int x=0,q=1; RG char ch=getchar();
15 while ((ch<0 || ch>9) && ch!=-) ch=getchar(); 16 if (ch==-) q=-1,ch=getchar(); 17 while (ch>=0 && ch<=9) x=x*10+ch-48,ch=getchar(); 18 return q*x; 19 } 20 21 int main(){ 22 #ifndef ONLINE_JUDGE 23 freopen("Pakowanie.in","r",stdin); 24 freopen("Pakowanie.out","w",stdout); 25 #endif 26 n=gi(),m=gi(); 27 for (RG int i=0;i<n;++i) a[i]=gi(),bin[1<<i]=i; 28 for (RG int i=1;i<=m;++i) c[i]=gi(); 29 sort(c+1,c+m+1),reverse(c+1,c+m+1),m=min(m,n+1); 30 for (RG int S=1,all=1<<n;S<all;++S){ 31 f[S]=inf,g[S]=m+1; 32 for (RG int i=S,s,x,res,tim;i;i^=s){ 33 s=lb(i),res=f[S^s],tim=g[S^s]; 34 if (res==inf) continue; x=bin[s]; 35 if (res+a[x]<=c[tim]) res+=a[x]; 36 else if (tim==m || c[tim+1]<a[x]) continue; 37 else res=a[x],++tim; 38 if (g[S]>tim) f[S]=res,g[S]=tim; 39 else if (g[S]==tim && f[S]>res) f[S]=res; 40 } 41 } 42 if (f[(1<<n)-1]==inf) puts("NIE"); 43 else cout<<g[(1<<n)-1]; return 0; 44 }

bzoj3717 [PA2014]Pakowanie