【BZOJ3717】[PA2014]Pakowanie 状压DP
【BZOJ3717】[PA2014]Pakowanie
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
题解:一开始想到了栅栏那题,以为是爆搜+剪枝,结果狂TLE不止,后来发现是状压。。。
显然我们应该贪心的从容量较大的包开始选,然后用f[S]表示已经用过的物品状态为S时,最少用几个包,g[S]表示在用的包最少的前提下,最后一个包的最大剩余容量。
转移时用到了一些卡常小技巧~
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n,m,N; int f[1<<24],g[1<<24],w[1<<24],c[110]; int main() { scanf("%d%d",&n,&m),N=(1<<n)-1; int i,j,k,S; for(i=0;i<n;i++) scanf("%d",&w[i]); for(i=n-1;i>=0;i--) w[1<<i]=w[i]; for(i=1;i<=m;i++) scanf("%d",&c[i]); sort(c+1,c+m+1); f[0]=m+1,g[0]=0; for(i=1;i<=N;i++) { for(j=i;j;j-=k) { k=j&-j,S=i^k; if(g[S]>=w[k]&&(f[S]>f[i]||(f[S]==f[i]&&g[S]>g[i]+w[k]))) f[i]=f[S],g[i]=g[S]-w[k]; else if(c[f[S]-1]>=w[k]&&(f[S]>f[i]+1||(f[S]==f[i]+1&&c[f[i]]>g[i]+w[k]))) f[i]=f[S]-1,g[i]=c[f[i]]-w[k]; } } if(!f[N]) printf("NIE"); else printf("%d",m+1-f[N]); return 0; }
| 欢迎来原网站坐坐! >原文链接<