动态规划-01背包

16 篇文章 0 订阅
14 篇文章 0 订阅

动态规划

01背包

题目描述

       现有编号分别为1 2 3 4 5的五件物品,它们的重量分别是2 2 6 5 4,价值分别是6 3 5 6 4,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?

物品重量w[] = {2, 2, 6, 5, 4}; 物品价值v[] = {6, 3, 5, 6, 4}; 背包容量为c

假设opt[i][j]表示前i件物品放入容量为j的背包中可获得的最大价值,则对于第i件物品来说,有两种情况:

(1) w[i] > c时,无法放入背包,此时opt[i][j] = opt[i-1][j] 

(2) w[i] <= c时,对于第i件物品来说有两种选择:放入背包或不放;

        a. 不放:opt[i][j] = opt[i-1][j];前i件物品可获得的最大价值即是前(i-1)件物品放入容量为j的背包中获得的最大价值;

        b. 放入:opt[i][j] = opt[i-1][j-w[i]]+v[i];前i件物品可获得的最大价值即是前(i-1)件物品放入容量为(j - w[i])的背包中获得的最大价值加上第i件物品的价值。

综上,01背包的状态转换方程如下:

代码如下:

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int main()
{
	int n;  //  物品数量
	int c;  //  背包容量
	cin >> n >> c;
	vector<int> v(n + 1, 0);  //  物品价值,首元素无意义,从v[1]开始
	vector<int> w(n + 1, 0);  //  物品重量,首元素无意义,从w[1]开始
	for (int i = 1; i <= n; i++)
		cin >> v[i];
	for (int i = 1; i <= n; i++)
		cin >> w[i];
	vector<vector<int>> opt(n + 1, vector<int>(c + 1, 0));
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= c; j++)
		{
			if (w[i] > j)
				opt[i][j] = opt[i - 1][j];
			else
				opt[i][j] = max(opt[i - 1][j], opt[i - 1][j - w[i]] + v[i]);
		}
	}
	cout << opt[n][c] << endl;
	return 0;
}

       上述方法的时间复杂度和空间复杂度均为O(nc),其中空间复杂度是可以优化到O(c)的。

      由状态转换方程和状态表可知,第i次循环得到的最大价值总和是由第(i-1)次循环递推而来的;即:opt[i][j]是由opt[i-1][j]和opt[i-1][j-w[i]]+v[i]两个子问题递推得到的。要想得到opt[j] (背包容量为j时可获得的最大价值总和),则第二个for循环语句中我们需要让j从容量c递减至1的递减顺序来计算opt[j],这样才能保证计算opt[j]时opt[j-w[i]]保存的是状态opt[i-1][j-w[i]]的值。(若还是让j从1->n递增,那么前面的opt[j]会覆盖计算后面的opt[j]值时前一次的状态值)。

综上,状态转换方程如下:

代码如下:

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int main()
{
	int n, c;
	cin >> n >> c;
	vector<int> v(n + 1, 0);
	vector<int> w(n + 1, 0);
	for (int i = 1; i <= n; i++)
		cin >> v[i];
	for (int j = 1; j <= n; j++)
		cin >> w[j];
	vector<int> opt(c + 1, 0);
	for (int i = 1; i <= n; i++)
	{
		for (int j = c; j >= 1; j--)
		{
			if (w[i] <= j)
				opt[j] = max(opt[j], opt[j - w[i]] + v[i]);
		}
	}
	cout << opt[c] << endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值