1. 程式人生 > >01背包 完全背包 算法解析

01背包 完全背包 算法解析

那種 lin ont cout 一次 背包問題 tle 答案 兩種

01背包問題

問題描述

 有n種物品,每種只有一個,第 i 種 物品的體積為Vi ,重量為 Wi。選一些物品裝到一個容量為C的背包,使得總體積不超C的情況下,重量盡量大。

問題分析

這個問題可以把每一件物品視作一次決策,每次決策只有選與不選兩種選擇。

我們設d[ i ][ j ]為第1件物品到第 i 件物品,放到載重為 j 的背包中的最大價值。

因此狀態轉移方程為

\[d[i][j] = \max (d[i - 1][j],d[i - 1][j - w[i]] + v[i])\]

我們可以寫出代碼

for (int i = 1; i <= n; i++) {
	for (int j = 0; j <= c; j++) {
		d[i][j] = (i == 1 ? 0 : d[i - 1][j]);
		if
(j >= W[i]) d[i][j] = max(d[i][j], d[i - 1][j - W[i]] + V[i]); } }

那麽我這個算法還可以在空間復雜度上優化一下嗎?如果我們能把數組d改為一維數組,那肯定再好不過了。

但是要保證,每一階段的決策,我們要在修改上一階段的值後,不在使用她的值。

我們可以用一個樣例運行一下這個代碼。

3 5
2 3
3 5
4 7

for (int i = 1; i <= n; i++) {
	for (int j = 0; j <= c; j++) {
		d[i][j] = (i == 1 ? 0 : d[i - 1][j]);
		if
(j >= W[i]) { d[i][j] = max(d[i][j], d[i - 1][j - W[i]] + V[i]); cout << "更改d" << j << " 使用" << j - W[i] << endl; } } cout << endl; }

技術分享圖片

我們發現我們當我們用上一個階段來修改當前階段的值時,上述算法,會出現“交叉”的情況。

比如我們修改了d[i][2],但是第七行我們就用到了,d[i-1][2],因此如果我們想把d設為一維數組,這樣得出的答案肯定錯的,因為這時候的d[2]已經不是上一個階段的d[2]了。

如果我們把第二層循環的循環方向修改一下,是不是會有所改善?

for (int i = 1; i <= n; i++) {
	for (int j = c; j >= 0; j--) {
		d[i][j] = (i == 1 ? 0 : d[i - 1][j]);
		if (j >= W[i]) {
			d[i][j] = max(d[i][j], d[i - 1][j - W[i]] + V[i]);
			cout << "更改" << j << " 使用" << j - W[i] << endl;
		}
	}
	cout << endl;
}

技術分享圖片

這一次打印的結果,沒有出現剛才那種“交叉”情況(使用前,修改了對應標號的值)。

最終經過空間優化後的算法

for (int i = 1; i <= n; i++) {
	for (int j = c; j >= 0; j--) {
		if (j >= W[i])
			d[j] = max(d[j], d[j - W[i]] + V[i]);
	}
}

完全背包問題

問題描述

有n種物品,每種有無窮多個,第 i 種 物品的體積為Vi ,重量為 Wi。選一些物品裝到一個容量為C的背包,使得總體積不超C的情況下,重量盡量大。

題目分析

01背包問題中,我們所作的決定只是拿與不拿的選擇,而完全背包問題,我們還要考慮拿幾個的問題。

\[d[i][j] = \max (d[i - 1][j],d[i - 1][j - k*w[i]] + k*v[i])\]

for (int i = 1; i <= n; i++) {
	for (int j = c; j >= 0; j--) {
		d[i][j] = (i == 1 ? 0 : d[i - 1][j]);
		for (int k = 0; k * W[i] <= j; k++) {
			d[i][j] = max(d[i][j], d[i - 1][j - k * W[i]] + k * V[i]);
		}
	}
}

那我們還能像01背包問題一樣優化嗎,當然可以。

for (int i = 1; i <= n; i++) {
	for (int j = W[i]; j <= c; j++) {
		d[j] = max(d[j], d[j - W[i]] + V[i]);
	}
}

那麽現在我們可以發現,內層循環是順序的,而01背包問題是逆序的,這兩者有什麽區別和聯系嗎?

01背包問題,我們需要用上一階段的值,來更新本階段的值,而背包問題是重量小的狀態來更新質量大的狀態(因為j-w[i]),因此逆序保證了更新的過程中,不會更改接下來要用到的上一階段的值。

完全背包問題,我們需要用本階段的值,來更新本階段的值,因此要順序。

01背包 完全背包 算法解析