題意
你有n個物品和m個包。物品有重量,且不可被分割;包也有各自的容量。要把所有物品裝入包中,至少需要幾個包?
Input
第一行兩個整數n,m(1<=n<=24,1<=m<=100),表示物品和包的數量。
第二行有n個整數a[1],a[2],…,an,分別表示物品的重量。
第三行有m個整數c[1],c[2],…,cm,分別表示包的容量。
思路:
首先我們可以貪心地選取前幾大的包,(由於物品數<=24,其實只用考慮前24個包即可)
考慮到24的數據 狀壓顯得理所當然。
這題需要維護兩個東西,
一個是zhi[s]表示裝狀態爲s的物品需要的最小的包數,
但是這樣沒法轉移,於是還要維護, h[s]表示對應zhi[s],最後一個包剩下的空間。
zhi[s]從zhi[t]而來(t爲s任意去掉一個1),若zhi[t]+1值更小,更新,或者zhi[t]+1==zhi[s]值相同,h[t]更大,更新
可是爲什麼這樣就可以了
考慮一個狀態,其實在轉移過程中,你考慮了這個狀態的每一種放物品順序
比如取物品2.4 在狀態轉移zhi[10] 時 肯定是從 zhi[2] 和zhi[8] 分別轉移過來的
這就是和貪心的區別(感覺這就是狀壓的妙處)
#include<bits/stdc++.h>
using namespace std;
int n,m,h,h1,f,i1,i2,zhi[20000000],ret[20000000],w[20000000],sum[1000],a[100];
bool cmp(int a1,int b1)
{ return a1>b1;}
int main()
{
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=m;i++) cin>>sum[i];
sort(sum+1,sum+1+m,cmp);
for (int i=1;i<1<<n;i++)
w[i]=w[i>>1]+1;
memset(zhi,0x3f,sizeof(zhi));
zhi[0]=0;
for (int i=1;i<1<<n;i++)
{ i2=i;
for (int j=w[i];j;j=w[i2])
{ f=0;
i2=i2-(1<<(j-1));
i1=i-(1<<(j-1));
//cout<<i<<' '<<i1<<' '<<i2<<endl;
if (a[j]<=ret[i1]) {h=zhi[i1]; h1=ret[i1]-a[j];}
else {h=zhi[i1]+1;
if (sum[h]<a[j] || h>m) f=1;
h1=sum[h]-a[j];}
if(!f)
{if (h<zhi[i]) { zhi[i]=h; ret[i]=h1;}
else { if (zhi[i]==h) ret[i]=max(ret[i],h1);}}
}
}
if (zhi[(1<<n)-1]<=m) cout<<zhi[(1<<n)-1]<<endl; else cout<<"NIE"<<endl;
}
小拓展以示區別 揹包問題
放一個我覺得有代表意義的
多重揹包問題:
多重揹包問題描述:有編號分別爲a,b,c的三件物品,它們的重量分別是1,2,2,它們的價值分別是6,10,20,他們的數目分別是10,5,2,現在給你個承重爲 8 的揹包,如何讓揹包裏裝入的物品具有最大的價值總和?
多重揹包和01揹包、完全揹包的區別:多重揹包中每個物品的個數都是給定的,可能不是一個,絕對不是無限個。
有兩種解法,解題思路:
- 作爲一個新問題考慮,由於每個物品多了數目限制,因此初始化和遞推公式都需要更改一下。初始化時,只考慮一件物品a時,f[1][j] = min{num[1], j/weight[1]}。 計算考慮i件物品承重限制爲y時最大價值f[i][y]時,遞推公式考慮兩種情況,要麼第 i 件物品一件也不放,就是f[i-1][y], 要麼第 i 件物品放 k 件,其中 1 <= k <= (y/weight[i]),考慮這一共 k+1 種情況取其中的最大價值即爲f[i][y]的值,即f[i][y] = max{f[i-1][y], (f[i-1][y-k*weight[i]]+k*value[i])}。 這裏爲什麼不能像完全揹包一樣直接考慮f[i][y-weight[i]]+value[i]呢?因爲這樣不容易判斷第 i 件物品的個數是否超過限制數量 num[i]
其實就是普通dp,前不久瞭解到它是NPC問題,而之所以我們能o(n*m)解決,是因爲限制了揹包容量大小。