背包问题(二)——完全背包问题

之前我们已经介绍了0/1背包问题,现在我们以洛谷P1616为例,介绍一下完全背包问题

完全背包问题就是将0/1背包问题中的每样物品只能拿一次这个限制条件去掉,每样物品可以无限次装入。

对于完全背包的图形解释,我截取《LeetCode_101》内的解释展现出来:

简要说一下推导过程:因为我们可以多次拿取物品,在总容积不超过j的情况下,我们也最多只能装j/v[i]=k个物品,那么状态转移方程就写为

dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i],dp[i-1][j-2*v[i]]+2w[i],...dp[i-1][j-k*v[i]])。

利用0/1背包思想,我们可以得到

dp[i][j-v]=max( dp[i-1][j-v] , dp[i-1][j-2v]+w[i],dp[i-1][j-3v]+2w[i], dp[i-1][j-4v]+3w[i],..., dp[i-1][j-kv]+(k-1)w[i])。两边同时加上w[i]后可以替换上式中的max(dp[i-1][j-v[i]]+w[i],dp[i-1][j-2*v[i]]+2w[i],...dp[i-1][j-k*v[i]]).

所以我们就得到了状态转移方程——dp[i][j]=max(dp[i-1][j],dp[i][j-v[i]]+w[i])

同样的,我们可以对上述dp数组进行状态压缩,将第一个维度去掉。思路与0/1背包问题基本一致。

压缩后的状态转移方程:

dp[j]=max(dp[j],dp[j-w[i]]+w[i])

代码可写为:

for(int i=0;i<N;i++)
    for(int j=w[i];j<W;j++)
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

注意此时我们这里与0/1背包问题不同,此时我们是正向遍历数组。因为我们需要利用到j-w[i]列的信息。注意此时右边的dp[j]是第i-1行的值。

因此,介绍完完全背包问题后,洛谷的这道编程题就十分容易了:

#include<iostream>
#include<vector>
#include<math.h>
using namespace std;

int main() {
	int t, m;
	cin >> t >> m;

	vector<long long> dp(t + 1, 0); //数据较大,有可能爆int,所以开long long。
	long long* times = new long long[m];
	long long* values = new long long[m];
	int tim, val;
	for (int i = 0; i < m; i++) {
		cin >> tim >> val;
		times[i] = tim;
		values[i] = val;
	}
	for (int i = 1; i <= m; i++) {
		for (int j = times[i-1]; j <= t; j++)
			dp[j] = max(dp[j], dp[j - times[i-1]] + values[i-1]);
	}

	cout << dp[t];
	delete[]times;
	delete[]values;
	return 0;
}

 

posted @ 2022-02-25 22:03  天涯海角寻天涯  阅读(1002)  评论(0编辑  收藏  举报