[狀壓dp][BZOJ3717][PA2014]Pakowanie

題意

你有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揹包、完全揹包的區別:多重揹包中每個物品的個數都是給定的,可能不是一個,絕對不是無限個。

有兩種解法,解題思路:

  1. 作爲一個新問題考慮,由於每個物品多了數目限制,因此初始化和遞推公式都需要更改一下。初始化時,只考慮一件物品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)解決,是因爲限制了揹包容量大小。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章