0 1揹包問題及變種

2021-06-25 13:17:38 字數 3367 閱讀 9742

0-1揹包問題:

有n件物品和乙個容量為v的揹包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。

這個問題的特點是:每種物品只有一件,可以選擇放或者不放。

演算法基本思想:

利用動態規劃思想 ,子問題為:f[i][v]表示前i件物品恰放入乙個容量為v的揹包可以獲得的最大價值。

其狀態轉移方程是:f[i][v]=max   //這個方程非常重要,基本上所有跟揹包相關的問題的方程都是由它衍生出來的。

解釋一下上面的方程:「將前i件物品放入容量為v的揹包中」這個子問題,如果只考慮第i件物品放或者不放,那麼就可以轉化為只涉及前i-1件物品的問題,即1、如果不放第i件物品,則問題轉化為「前

i-1件物品放入容量為v的揹包中」;2、如果放第i件物品,則問題轉化為「前

i-1件物品放入剩下的容量為v-c[i]的揹包中」(此時能獲得的最大價值就是f [i-1][v-c[i]]再加上通過放入第i件物品獲得的價值w[i])。則f[i][v]的值就是1、2中最大的那個值。

(注意:f[i][v]有意義當且僅當存在乙個前i件物品的子集,其費用總和為v。所以按照這個方程遞推完畢後,最終的答案並一定是f[n] [v],不一定是唯一的最大值,也可以是f[n][0..v]的最大值。

以下為演算法**:是個不斷優化的過程。首先(1)給出了遞迴和迭代的思想,(2)然後對迭代的演算法**進行空間優化。(3)然後給出了該問題的變種:恰好裝滿揹包和完全揹包問題。

(1)遞迴方法:

// 0-1 揹包問題遞迴方法  回溯求最佳解

int packagerecursion(int c, int n)

(2)迭代方法:

//  0-1 揹包問題迭代方法 

void packageiteraction()

int j;

for(i = 0 ; i < n+1; i++) }

// 正好裝滿揹包 加上這幾行

/*for(i = 0 ; i < n+1; i++)

}*/for(i = 1; i < n+1; i++) }

if(f[n][c] > 0)

}} else

for(i = 0; i < n+1; i++) // 釋放記憶體空間

delete f;

}

(3)迭代方法優化空間複雜度:

以上迭代方法的時間複雜度和空間複雜度都是o(n*v),其中時間複雜度基本已經不能再優化了,但空間複雜度卻可以優化到o(v)。此時的做法是用f[v]來儲存當前獲得的價值的最大值,第二個for迴圈反向遍歷。

//  將j=v:weight[i] 只用一維陣列儲存就可以了,降低了空間複雜度

void packageiteractionopt()

} cout << f[c] << endl;

delete f; // 釋放記憶體空間

}

main函式**:

int main()

輸出結果:

(4)變種

1:恰好裝滿揹包

在初始化時除了f[0]為0其它f[1..v]均設為-∞,這樣就可以保證最終得到的f[v]是一種恰好裝滿揹包的最優解,即揹包中存有的價值,不是揹包的容量。如果不能恰好滿足揹包容量,即不能得到f[v]的最優值,則此時f[v]=-∞,這樣就能表示沒有找到恰好滿足揹包容量的最優值。

以上**中可以將注釋的部分加上就可以了。其中int型別中最小值為0x80000000.

2:完全揹包

/*完全揹包問題  */

#include #include //a題時候使用memset需要新增這個庫函式

using namespace std;

int main()

}// 錯誤**

/*如樣例 2 10 2 3 3 5

*//*int k;

for(i = 1; i < n+1; i++)

}*/cout << f[m] << endl;

delete f; }}

// 測試輸入

/*5 1000

144 990

487 436

210 673

567 58

1056 897*/

// 輸出

// 5940

參考文獻
2:
3:
「揹包題目」的基本描述是:有乙個揹包,能盛放的物品總重量為s,設有n件物品,其重量分別為w1,w2,…,wn,希望從n件物品中選擇若干物品,所選物品的重量之和恰能放進該揹包,即所選物品的重量之和即是s。遞迴和非遞迴解法都能求得「揹包題目」的一組解,試寫出「揹包題目」的非遞迴解法

分析:(1)遞迴解法直接用回溯就可以了,我們看**:

// 回溯

void backtracking(int depth, int maxdepth, vectortemp, vectorvec, const int &s)

}if(sum == s)result.push_back(tmp);

return;

} backtracking(depth+1, maxdepth, temp, vec, s); // 沒有取depth的元素時的遞迴

temp[depth]=1;

backtracking(depth+1, maxdepth, temp, vec, s); // 取depth的元素時的遞迴

}

(2)非遞迴求出所有的解,我們可以採用位操作:

// 找出所有的解

void package(vectorvec, int s)

for(int i = 0; i < n+1; i++)

f[i][0] = 1; // 初始化

for(int i = 1; i < n+1; i++)

} for(int i = 1; i < n+1; i++)

cout << endl;

} // 回溯求結果

int j = s;

for(int i = n; i > 0; i--)}}

}

0 1揹包問題的變種

最簡單的思路是,由於物品是可以無限使用的,但是揹包的容量是有限的,所以,其實每個物品可以取到的數量是有最大值的。換句話說,可以將這個可以無限使用的物品的揹包問題,轉換成乙個有限使用的物品的揹包問題,只不過此時在選取的物品列表中,很多物品是重複的。只此而已,這是乙個解決思路。更進一步,其實可以優化這個...

變種 揹包問題 演算法設計 揹包問題

題目 乙個旅行者準備隨身攜帶乙個揹包,可以放入揹包的物品有n種,每種物品的重量和價值分別為wj,vj 如果揹包的最大重量限制是b,怎樣選擇放入揹包的物品以使得揹包的價值最大?目標函式 約束條件 演算法設計 設fk y 表示只允許裝前k 種物品,揹包總重不超過y 時揹包的最大價值。fk y 有兩種情況...

揹包問題 01揹包問題

n個物品,總體積是v,每個物品的體積的vi,每個物品的最大價值是wi,在不超過v的體積下求最大價值 eg揹包容積為 5 物品數量為 4 物品的體積分別為 物品的價值分別為 思路定義乙個二位陣列int f new int n 1 v 1 f i j 就表示在1 i個物品中選取體積小於v的情況的最大價值...