2021-02-25动态规划——0/1背包问题

摘要:

0/1背包问题——动态规划模板


问题分析:

原题链接:P1048采药
孪生兄弟:完全背包问题


算法:

动态规划是将问题不断细分,将大问题划分为小问题,再逐步解决的方法。满足无后效性和最优子结构。动态规划应用最明显的特征就是每个子问题都仅仅求解一次,并在求解之后将结果保留。在下一次遇到同样的问题时可以直接得出答案。

0/1背包问题是指有m中物品,每一种物品都有其对应的价值。在背包体积不超过n的情况下,装走最大价值的物品,且每一种物品都最多选取一次。由于每一次选取物品后的最大价值,都和上一次选取后的总价值有关,因此该问题具有最优子结构,可用动态规划求解。

具体的状态转移方程见官方的网站的题目解答洛谷P1048采药——题目解答

这里只强调一点,就是代码中内层循环要逆着遍历,从大到小。如果是从小到大,即从1到n,则对应另一种动态规划问题——完全背包问题(一件物品可以重复多次选择)

另一个细节就是,本道题可以采用二维数组的方式进行,即ans[m][n],其中m是物品的件数,n是总时间限制。那么任何一个元素ans[i][j]则表示在前i件物品中用时长为j的时间获得的最大利润

当然,更高效的是可以采用一维数组的方式进行求解。代码基本一样 ,但是意义没有二位数组明显。即ans[n]中的每一个元素ans[i]表示在时间为i的前提下,获得的最大利润,可以从n件物品中任意选取

但是,无论是一维还是二维,其本质思想都是根据状态转移方程不断更新,得到最优值。


代码以及详细注释:

方法一:采用一维数组

#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
#pragma warning(disable:4996)

class Solution {
public:
	int n, m;
	vector<pair<int, int>> w;
	vector<long long int> ans;

	void dynamic_programming() {
		cin >> n >> m;
		w.resize(m + 1);
		ans.resize(n + 1,0);
		for (int i = 1; i <= m; ++i)
			cin >> w[i].first >> w[i].second;
		for (int i = 1; i <= m; ++i)
		{
			for (int j = n; j >0; --j)
			{
				if (j >= w[i].first)
					ans[j] = max(ans[j], ans[j - w[i].first] + w[i].second);
			}
			/*for(int i=1;i<=n;++i)
				cout << ans[i] << " ";
			cout << endl;*/
		}
		cout << ans[n]<<endl;
	}
};

int main() {
	//freopen("in.txt", "r", stdin);
	Solution s;  
	s.dynamic_programming();
	return 0;
}

方法二:二维数组求解:

#include <iostream>
#include <stdio.h>
#include <vector>
#pragma warning(disable:4996)
using namespace std;


class Solution {
public:
	int n, m;  //n是总时间 ,m是药品的件数
	vector<pair<int,int>> w;
	vector<vector<int>> ans;  //利用二维网格搜索
	void dynamic_programming() {
		cin >> n >> m;
		w.resize(m + 1);
		ans.resize(m + 1, vector<int>(n+5, 0));
		for (int i = 1; i <= m; ++i) {
			int x, y;
			cin >> x >> y;
			w[i] = { x,y };
		}
		for (int i = 1; i <= m; ++i) {
			for (int j = 1; j<=n; ++j) {
				if (j >= w[i].first)
					ans[i][j] = max(ans[i - 1][j], ans[i - 1][j - w[i].first] + w[i].second);
				else
					ans[i][j] = ans[i - 1][j];
			}
		}
		cout << ans[m][n];
	}
};

int main()
{
	//freopen("in.txt", "r", stdin);
	Solution s;
	s.dynamic_programming();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Sentry-X

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

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

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

打赏作者

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

抵扣说明:

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

余额充值