動態規劃之揹包問題
阿新 • • 發佈:2018-12-16
1. 一維揹包問題
0-1揹包問題
給定一個載重量為的揹包,有個物品,每個物品的重量為,每個物品的價值為,如何往揹包中裝物品使得揹包中的總價值最大化。
可以用動態規劃的方法對該問題進行求解。
1.1. 遞推表示式
用表示前物品中選擇物品放入揹包使得總重量不超過,揹包的最大總價值。因此有如下所示的遞推公式:
(1) 解釋:
揹包的最大總價值有兩種情況可以達到,一是,不放第件物品,前件物品已經把揹包填滿了,放不下第件物品了,那該值就是;二是,放第件物品,那麼前件物品所佔的空間只能是,那該值就是。因此,就是這兩個值中的較大值。
1.2. 動態規劃實現
int maxValue(const std::vector<int>& weights, const std::vector<int>& values, const int& C) {
std::vector<std::vector<int> > dp(weights.size() + 1, std::vector<int>(C + 1, 0));
for (int i = 1; i < dp.size(); ++i) {
for (int j = 1; j < dp[i].size(); ++j) {
if (j >= weights[i-1])
dp[i][j] = max(dp[i-1][j], dp[i-1][j - weights[i-1]] + values[i-1]);
}
}
return dp[weights.size()][C];
}
時間複雜度為,空間複雜度也為。
1.3. 降低空間複雜度
能不能降低空間複雜度呢?答案是肯定的。再觀察式(1),可以發現的值僅和有關。因此可以壓縮這個維度,僅用一個一維陣列表示。
int maxValue(const std::vector<int>& weights, const std::vector<int>& values, const int& C) {
std::vector<int> dp(C + 1, 0);
for (int i = 1; i < weights.size(); ++i) {
// reverse order
for (int j = C; j >= 1; --j) {
if (j >= weights[i-1])
dp[j] = max(dp[j], dp[j - weights[i-1]] + values[i]);
}
}
return dp[C];
}
注意在內層迴圈中使用倒序,因為在dp[j]的取值會影響其後面的取值,如果從前向後遍歷,則前面的值已經重新整理了,會影響後面的值,因此在遍歷時要採用倒序。
2. 二維揹包問題
For now, suppose you are a dominator of m 0s and n 1s respectively. On the other hand, there is an array with strings consisting of only 0s and 1s.
Now your task is to find the maximum number of strings that you can form with given m 0s and n 1s. Each 0 and 1 can be used at most once.
2.1 遞迴公式
以表示在前字串中,0不超過個,1不超過個條件下,最多的字串數量。
(2)
同樣地,可以利用二維陣列實現動態規劃的演算法以降低空間複雜度。
int findMaxForm(vector<string>& strs, int m, int n) {
if (strs.empty()|| m < 0 || n < 0) return 0;
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
// dynamic programming
for (int i = 0; i < strs.size(); i++) {
int zeros = 0, ones = 0;
for (char c : strs[i]) {
if (c == '0') zeros++;
if (c == '1') ones++;
}
for (int j = m; j >= zeros; j--) {
for (int k = n; k >= ones; k--) {
dp[j][k] = max(dp[j][k], dp[j-zeros][k-ones] + 1);
}
}
}
return dp[m][n];
}