0 1背包 填表实现
01背包問題是一個經典的問題了,接下來使用動態規劃思想,填表法來解決一下這個問題吧。
填表法就基本的操作步驟 :
第一步:分析問題的狀態變化,將在所有物品里得到的最大價值分割成子問題,子問題:只有一個物品的時候,得到的最大價值,有兩個物品的時候得到的最大價值,隨著物品數不斷增加,每增加一個物品得到的最大的價值。
只有一個物品的時候,很容易得到最大的價值,升級為兩個物品呢,這時候就有取舍了,就是在袋子當前的容量下要不要將物品放入袋子中?首先取決于,物品能不能放進袋子,如果能放進袋子里,價值怎么來衡量?帶著這些問題,去表格中尋找規律吧。
第二步:分析問題,確定初值,針對這個問題很容易想到背包空間為0的時候,里面的價值為0。
第三步:分析分析問題,確定表格的形式,橫軸縱軸各代表什么含義。
首先聲明一點,填表的過程你也不知道規律是什么,完全按照題目的規則去填表,當然如何設計表格也是要一定的題量之后可能會涌現靈感,或者在分析問題階段,按在子問題的方式設計表格。比如這道題,每個子問題都是增加一個物品之后的到的最大價值,這樣確定了表格的一個軸,即物品數,另一個軸代表不同背包容量下的最大價值。
| name | weight | value | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| a | 2 | 6 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
| b | 2 | 3 | 0 | 3 | 3 | 6 | 6 | 9 | 9 | 9 | 10 | 11 |
| c | 6 | 5 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 11 |
| d | 5 | 4 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 10 | 10 |
| e | 4 | 6 | 0 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
?
上圖是復制的,懶得畫了,相信已經解釋的很清楚了,這個圖是含義是。有物品a,b,c,d,e, 重量和價值如圖,從下往上填表,每一行代表:包括這個行和它下面的這寫物品可選。
找一下上面拋出問題的答案吧,從倒數第二行看,背包容量不夠裝當前物品的時候,那就看看以前裝填價值的情況即下面的值,當可以裝上當前物品的時候,那么它肯定會占用其他物品的空間呢,我們嘗試裝進去,去看看將背包剩余空間還能裝多少價值,這就是為什么要將背包容量也分割成子問題的原因了,不然到時候去哪查呀,剩余空間能裝的價值當然還要從當前行的下一行找,因為下一行不包含當前的物品。因為是嘗試裝,裝完得到值還要和不裝的時候比大小吧。到此傳說中的狀態轉移方程是不是清晰了:
dp[i][j]? = max(dp[i-1][j] , dp[i-1][j- w[i]]+v[i])? ?w表示重量 v 表示價值。
當然如果不夠裝,那就不裝了唄,所以dp[i-1][j- w[i]]? >=0? 和 <0 的情況,
鏈接:https://www.nowcoder.com/questionTerminal/708f0442863a46279cce582c4f508658
來源:牛客網
現有一個容量大小為V的背包和N件物品,每件物品有兩個屬性,體積和價值,請問這個背包最多能裝價值為多少的物品?
輸入描述:
第一行兩個整數V和n。 接下來n行,每行兩個整數體積和價值。1≤N≤1000,1≤V≤20000。 每件物品的體積和價值范圍在[1,500]。輸出描述:
輸出背包最多能裝的物品價值。 #include <iostream> #include <string> #include <vector> #include <algorithm> using namespace std; int main() {int v = 0;int n = 0;cin >> v >> n;vector<vector<int>> m_v;//vector<int> t_tmp;//m_v.push_back(t_tmp);for(int i=0;i<n;++i){int a = 0;int b = 0;cin >> a >> b;vector<int> tmp;tmp.push_back(a);tmp.push_back(b);m_v.push_back(tmp);}vector<vector<int>> ret(n, vector<int>(v+1, 0));for (int i = 0; i < v + 1; ++i){if (i >= m_v[0][0]){ret[0][i] = m_v[0][1];}}for (int i = 1; i < n; ++i){for (int j = 0; j <= v; ++j){int mem = 0;if (j - m_v[i][0] >= 0){mem = ret[i - 1][j - m_v[i][0]];ret[i][j] = max(ret[i - 1][j], mem + m_v[i][1]);}else{ret[i][j] = ret[i - 1][j];}}}cout << ret[n-1][v];system("pause"); return 0; }我的表是從上到下的,不要迷了。
背包擴展:
https://leetcode-cn.com/problems/partition-equal-subset-sum/
給定一個只包含正整數的非空數組。是否可以將這個數組分割成兩個子集,使得兩個子集的元素和相等。
注意:
每個數組中的元素不會超過 100
數組的大小不會超過 200
示例 1:
輸入: [1, 5, 11, 5]
輸出: true
解釋: 數組可以分割成 [1, 5, 5] 和 [11].
?其實這個問題也是一個背包的問題,只是沒讓求最大的價值,而是恰當的選擇物品能不能剛好將背包的一一半裝滿,根據之前填表法的思路,我們將背包空間也做了遞增的去擴大,現在只需要將背包空間設置為,所有數字和的一半,(這里有個問題,如果所有的和為奇數,那么一定不符合要求),其實就是只有容量一個維度的限制,我們來看表吧,表里面的值表示,當前背包容量下,能裝的物品大小。
最終得到的值看看是不是背包容量的一半就行了。
class Solution { public:bool canPartition(vector<int>& nums) {int num = 0;for(auto &e:nums){num += e;}if(num%2){return false;}vector<vector<int>> dp(nums.size(),vector<int>(num/2+1,0));for(int i=0;i<=num/2;++i){if(i >= nums[0]){dp[0][i]=nums[0];}}for(int i=1;i<nums.size();++i){for(int j=0;j<=num/2;++j){if(j-nums[i]>=0){dp[i][j] = max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i]);}else{dp[i][j] = dp[i-1][j];}}}if(dp[nums.size()-1][num/2] == (num/2) ){return true;}else{return false;}} };?
總結
以上是生活随笔為你收集整理的0 1背包 填表实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: visual studio代码自动排版
- 下一篇: matlab 绘制 3d 心