1. 程式人生 > >0/1背包

0/1背包

[] amp 價值 表示 容易 應該 分析 背包問題 註意

0/1背包的問題模型如下:

  給定N個物品,其中第i個物品的體積為Vi ,價值為Wi 。有一容積為M的背包,要選擇一些物品放入背包,使物品體積不超過M的前提下,物品的價值總和最大。

dp[i][j]表示從前i個物品中選出了總體積為j的物品放入背包,物品的最大價值。即我們很容易得出解決的代碼:

memset(dp,0,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++)
            dp[i][j]=dp[i-1][j];
        for(int
j=v[i];j<=m;j++) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]); }

根據上面的代碼 我們可以看出 狀態方程的推導只和當前狀態以及上一狀態有關。所以我們可以用滾動數組優化

memset(dp,0,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++)
            dp[i&1][j]=dp[(i-1)&1][j];
        
for(int j=v[i];j<=m;j++) dp[i&1][j]=max(dp[i&1][j],dp[(i-1)&1][j-v[i]]+w[i]); }

這裏我們只要2*m的空間即可

其實進一步分析代碼,在每個階段的開始,我們實際上只是執行了一次從dp[i-1][]到dp[i][]的一次拷貝,也就是說我們可以進一步省略掉dp數組的第一維

即dp[j]表示背包中放入總體積為j的最大價值

memset(dp,0,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<=n;i++){
        
for(int j=m;j>=v[i];j--) dp[j]=max(dp[j],dp[j-v[i]]+w[i]); }

我們應該可以註意到,我們的第二重循環采用了倒序的方式,這是為了滿足0/1背包問題中每個物品是唯一的,只能放入一次的要求

0/1背包