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