动态规划之背包问题

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;
}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知行SUN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值