初識DP-01揹包問題及其空間優化
阿新 • • 發佈:2018-12-12
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;
}