動態規劃解0-1揹包問題
過程:
依次計算數組中的每一個值:當容量C超過了當前物品的重量時,說明當前物品可以放入揹包中,那麼就要考慮是否將該物品放入揹包中,如果放入揹包中,當前揹包的總價值就爲memo[i-1][j-w[i]]+v[i],如果不放入揹包中,當前揹包的總價值就爲memo[i-1][j],使用這兩種情況的最大值更新數組數據
代碼:
#include <iostream>
#include <vector>
using namespace std;
/*
[1,2,3]
[6,10,12]
解:22
*/
//動態規劃解
class Solution {
public:
int beibao(vector<int>& w,vector<int>& v, int C) {
int m = w.size();
if(m==0) return 0;
vector<vector<int> > memo(m,vector<int>(C+1,0));//存儲第[0,i]件物品在容量C下能獲得的最大總價值
for(int j=0; j<=C; j++)
memo[0][j]=(j>=w[0]?v[0]:0);
for(int i=1; i<m; i++)
{
for(int j=0; j<=C; j++)
{
memo[i][j] = memo[i-1][j];
if(j>=w[i]) //當前物品有機會裝到揹包中
{
//不選擇該物品和選擇該物品
memo[i][j] = max(memo[i-1][j],memo[i-1][j-w[i]]+v[i]);
}
}
}
return memo[m-1][C];
}
};
int main()
{
vector<int> w;
vector<int> v;
w.push_back(1);
w.push_back(2);
w.push_back(3);
v.push_back(6);
v.push_back(10);
v.push_back(12);
cout<<Solution().beibao(w,v,5)<<endl;
return 0;
}
時間複雜度:O(n*C)
空間複雜度:O(n*C)
優化1
兩行空間輪流去使用
#include <iostream>
#include <vector>
using namespace std;
//動態規劃解 優化1
class Solution {
public:
int beibao(vector<int>& w,vector<int>& v, int C) {
int m = w.size();
if(m==0) return 0;
vector<vector<int> > memo(2,vector<int>(C+1,0));//存儲第[0,i]件物品在容量C下能獲得的最大總價值
for(int j=0; j<=C; j++)
memo[0][j]=(j>=w[0]?v[0]:0);
//後一行的計算,只依賴於前一行,偶數行用第一行,奇數行用第二行
for(int i=1; i<m; i++)
{
for(int j=0; j<=C; j++)
{
memo[i%2][j] = memo[(i-1)%2][j];
if(j>=w[i]) //當前物品有機會裝到揹包中
{
//不選擇該物品和選擇該物品
memo[i%2][j] = max(memo[(i-1)%2][j],memo[(i-1)%2][j-w[i]]+v[i]);
}
}
}
return memo[(m-1)%2][C];
}
};
int main()
{
vector<int> w;
vector<int> v;
w.push_back(1);
w.push_back(2);
w.push_back(3);
v.push_back(6);
v.push_back(10);
v.push_back(12);
cout<<Solution().beibao(w,v,5)<<endl;
return 0;
}
優化2
是否可以只使用一行大小爲C的數組完成動態規劃:從右向左刷新格子內容
對於第2個物品,當容量爲5的時候,memo[j] = max(memo[j],memo[j-w[i]]+v[i]);
代碼
#include <iostream>
#include <vector>
using namespace std;
//動態規劃解 優化2
class Solution {
public:
int beibao(vector<int>& w,vector<int>& v, int C) {
int m = w.size();
if(m==0) return 0;
vector<int> memo(C+1,0);//存儲第[0,i]件物品在容量C下能獲得的最大總價值
for(int j=0; j<=C; j++)
memo[j]=(j>=w[0]?v[0]:0);
//從右向左更新數據
for(int i=1; i<m; i++)
{
for(int j=C; j>=w[i]; j--)
{
//當前物品有機會裝到揹包中
//不選擇該物品和選擇該物品
memo[j] = max(memo[j],memo[j-w[i]]+v[i]);
}
}
return memo[C];
}
};
int main()
{
vector<int> w;
vector<int> v;
w.push_back(1);
w.push_back(2);
w.push_back(3);
v.push_back(6);
v.push_back(10);
v.push_back(12);
cout<<Solution().beibao(w,v,5)<<endl;
return 0;
}
面試中的0-1揹包問題 LeetCode 416
題目
416. 分割等和子集
給定一個只包含正整數的非空數組。
是否可以將這個數組分割成兩個子集,使得兩個子集的元素和相等。
注意:
每個數組中的元素不會超過 100
數組的大小不會超過 200
示例 1:
輸入: [1, 5, 11, 5]
輸出: true
解釋: 數組可以分割成 [1, 5, 5] 和 [11].
示例 2:
輸入: [1, 2, 3, 5]
輸出: false
解釋: 數組不能分割成兩個元素和相等的子集.
思路
代碼
#include <iostream>
#include <vector>
using namespace std;
//動態規劃解
//可以看做一個典型的動態規劃問題,從數組中選取n個數,讓他們構成sum/2
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n=nums.size();
int sum=0;
for(int i=0; i<n; i++)
sum+=nums[i];
if(sum%2!=0) return false; //如果不是偶數,則肯定分割不了
sum = sum/2;
vector<vector<bool> > memo(n,vector<bool>(sum+1,false));//代表考慮[0,i]中的元素,是否可以組成j
for(int i=0; i<=sum; i++)
if(i==nums[0]) memo[0][i] = true;//只考慮第0個元素,是否可以組成sum
for(int i=1; i<n; i++)
{
for(int j=0; j<=sum; j++)
{
memo[i][j] = memo[i-1][j];
//對於當前的第i個數,可以是組成當前j的數,也可以不是
if(j>=nums[i])
memo[i][j] = memo[i-1][j] || memo[i-1][j-nums[i]];
}
}
return memo[n-1][sum];
}
};
int main()
{
return 0;
}