1. 程式人生 > >初識DP-01揹包問題及其空間優化

初識DP-01揹包問題及其空間優化

01揹包是經典的DP問題,個人是看這位的blog看懂的:點這裡~~~~~~
感覺這個講的十分詳細,很好理解。


借一個例題來示範吧(洛谷 P1049 裝箱問題)

題目描述:

有一個箱子容量為 V(正整數,0 ≤ V ≤ 20000),同時有n個物品(0< n ≤30),每個物品有一個體積(正整數)。

要求n個物品中,任取若干個裝入箱內,使箱子的剩餘空間為最小。

輸入格式:

1個整數,表示箱子容量
1個整數,表示有n個物品
接下來n行,分別表示這n個物品的各自體積

輸出格式:

1個整數,表示箱子剩餘空間。


為使箱子剩餘空間最小,那就是裝入的物品總體積要最大,典型的揹包問題,DP思想就不多說了。

先看無優化的程式碼(二維陣列):

#include<iostream> 
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
    int n, V, v[35];
    int dp[35][20005];
    int i, j;
    memset(dp, 0, sizeof(dp));   //初始化為0
    cin >> V >>
n; for (i = 1; i <= n; i++) cin >> v[i]; for (i = 1; i <= n; i++) for (j = 1; j <= V; j++) { if (v[i] <= j) //如果 i 還裝得下,就比較裝入 i 和不裝入 i 的收益。 dp[i][j] = max(v[i] + dp[i - 1][j - v[i]], dp[i - 1][j]); else dp[
i][j] = dp[i - 1][j]; //如果 i 裝不下,就繼承上一個的收益。 } cout << V - dp[n][V] << endl; return 0; }

· 優化

這裡可以進行空間優化,將二維陣列換為一維陣列,時間也能相對優化一些。
重點在於一維陣列 dp[200005] 的自更新,觀察一下這部分程式碼:

for (j = 1; j <= V; j++)
{
	if (v[i] <= j)
		dp[i][j] = max(v[i] + dp[i - 1][j - v[i]], dp[i - 1][j]);
	else
		dp[i][j] = dp[i - 1][j];
}

①可以看到當 v[i] > j 時,執行dp[i][j] = dp[i - 1][j],所以在一維陣列中,這些部分就不需要變動了 。

②再者,當 j <= v[i] 時,執行 dp[i][j] = max(v[i] + dp[i - 1][j - v[i]], dp[i - 1][j])
可以看到,以 j = 3為例,dp[i][3]只可能和dp[i-1][3]、dp[i-1][2]、dp[i-1][1]、dp[i-1][0] 有關,所以一維陣列只要從後向前更新,就不會有影響。

那麼優化後的這段程式碼為:

for (j = V; j >= v[i]; j--)    //從V開始,更新到 v[i] 停止
{
	dp[j] = max(v[i] + dp[j - v[i]], dp[j]);
}

以下優化後完整程式碼:

#include<iostream> 
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
    int n, V, v[35];
    int dp[20005];
    int i, j;
    memset(dp, 0, sizeof(dp));   //依舊要初始化
    cin >> V >> n;
    for (i = 1; i <= n; i++)
        cin >> v[i];
    for (i = 1; i <= n; i++)
    {
        for (j = V; j >= v[i]; j--)
        {
            dp[j] = max(v[i] + dp[j - v[i]], dp[j]);
        }
    }
    cout << V - dp[V]<< endl;
    return 0;
}