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

bzoj3717 [PA2014]Pakowanie 狀壓dp

Description


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

第一行兩個整數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),分別表示包的容量。

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

Solution


寫了一個隨機化過不去

考慮狀壓。我們設f[x]表示選取物品狀態為x時最少包的數量,g[x]表示選取物品狀態為x,答案為f[x]時最後一個包剩餘的最大空間
考慮轉移。我們列舉x中1的位置,然後把這個物品塞進包裡面就可以了

Code


#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[205],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("data.in","r",stdin); // freopen("myp.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("NIE"); else printf("%d\n", f[lim]); return 0; }