动态规划之背包问题
01背包问题
问题1 为什么01背包问题可以用一维数组来优化?
用二维数组枚举如下:
所以在枚举过程中仅用到上一行的数据,所以可以用一维数组来优化。
问题2 使用一维数组优化过程中为什么物体的重量要从大到小枚举?
我从大到小枚举如下:
我从小到大枚举如下:
code1
#include<iostream>
#define N 6
#define W 21
int B[N][W] = { 0 };
int w[6] = { 0, 2, 3, 4, 5, 9 };
int v[6] = { 0, 3, 4, 5, 8, 10 };
void knapsack() {
int k, C;
for (k = 1; k < N; k++) {
for (C = 1; C < W; C++) {
if (w[k] > C) {
B[k][C] = B[k - 1][C];
}
else {
int value1 = B[k - 1][C - w[k]] + v[k];
int value2 = B[k - 1][C];
if (value1 > value2) {
B[k][C] = value1;
}
else {
B[k][C] = value2;
}
}
}
}
}
int main() {
knapsack();
printf("%d\n", B[5][20]);
return 0;
}
code2
#include<iostream>
#include<algorithm>
#define N 1010
#define V 1010
using namespace std;
int n, m;
int B[N][N] = {0};
int v[N], w[N];
int main() {
cin >> n >> m;
for (int i = 1; i <=n; i++) cin >> v[i] >> w[i];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (w[i] > j) {
B[i][j] = B[i - 1][j];
}
else {
B[i][j] = max(B[i - 1][j - w[i]] + v[i], B[i - 1][j]);
}
}
}
cout << B[n][m] << endl;
return 0;
}
code3
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n, m;
int B[N][N];
int value[N], weight[N];
int main() {
cin >> n >> m;
for (int i = 1; i <=n; i++) cin >> weight[i] >> value[i];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
B[i][j] = B[i - 1][j];
if(j >= weight[i])
B[i][j] = max(B[i - 1][j - weight[i]] + value[i], B[i][j]);
}
}
int res = 0;
for (int i = 0; i <= m; i++) res = max(res, B[n][i]);
cout << res << endl;
return 0;
}
code(一维数组优化)
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n, m;
int B[N];
int value[N], weight[N];
int main() {
cin >> n >> m;
for (int i = 1; i <=n; i++) cin >> weight[i] >> value[i];
for (int i = 1; i <= n; i++) {
for (int j = m; j >= weight[i]; j--) {
B[j] = max(B[j - weight[i]] + value[i], B[j]);
}
}
cout << B[m] << endl;
system("pause");
return 0;
}
leetcode 416 分割等和子集
解法一:二维数组+动态规划
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
const int N = nums.size();
int target = sum >> 1;
if (sum & 1) return false;
//dp[i][j] means whether we can sum to j using first i numbers.
vector<vector<bool>> dp(N + 1, vector<bool>(sum + 1, false));
// every number can build number 0.
for (int i = 0; i <= N; ++i) {
dp[i][0] = true;
}
// but for position 0, it can build number nothing.
for (int j = 1; j <= target; ++j) {
dp[0][j] = false;
}
// anyway, position 0 can build number 0.
dp[0][0] = true;
for (int i = 1; i <= N; ++i) {
for (int j = 0; j <= target; ++j) {
if (j >= nums[i - 1])
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
else
dp[i][j] = dp[i - 1][j];
}
}
return dp[N][target];
}
};
解法二:一维数组+动态规划
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0), target = sum >> 1;
if(sum & 1) return false;
vector<int> dp(target+1, false);
dp[0] = true;
for(int num:nums){
for(int i = target; i >= num; i--){
dp[i] = dp[i]||dp[i-num];
}
}
return dp[target];
}
};
leetcode 474 一和零
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
for(string str : strs){
int zeros = 0, ones = 0;
for(char c : str)(c == '0') ? ++zeros : ++ones;
for(int i = m; i >= zeros; i--){
for(int j = n; j >= ones; j--){
dp[i][j] = max(dp[i][j], dp[i-zeros][j-ones]+1);
}
}
}
return dp[m][n];
}
};
leetcode 1049 最后一块石头的重量2
class Solution {
public:
// 因为挑选石头是任意的, 不能使用贪心法每次挑选重量最大的两块石头
// 第一次挑选a,b, 放回a-b, ....., 第n次挑选c,a-b, 放回c-a+b, 最终结果为(a+d+c+g)-(b+e+f)
// 因此, 可以视作一个0-1背包问题,将石头分为两堆,两堆重量之差最小是多少
// 背包容量为 sum/2, 每个石头拿起来或者不拿起来,能装下最多的石头重量是多少
int lastStoneWeightII(vector<int> &stones){
int sum = accumulate(stones.begin(), stones.end(), 0);
vector<bool> dp(sum / 2 + 1, false); // dp[i] - 是否可以找到一部分石头,其总重量为i
dp[0] = true; // dp[0] - 不拿石头时,其总重量为0
for(int i = 0; i < stones.size(); ++i)
for(int w = dp.size() - 1; w >= stones[i]; --w)
dp[w] = dp[w] | dp[w - stones[i]];
// 找到可以放的最大重量假设为i, 则两部分的差值为 (sum - i) - i
for(int i = dp.size() - 1; i >= 0; --i)
if(dp[i] == true)
return sum - 2 * i;
return sum;
}
};