C++多重背包

多重背包简介

题目描述:
给出N个物品(一个物品可以选择0~a个),背包最大承重为M,每个物品有一个重量w,一个价值v,一个个数a。如何选择才能在重量不超过M的情况下,使选择的物品的价值总和最大。

输入格式:
第一行:是两个正整数N,M。
第2 ~ (n + 1)行:每行三个正整数w,v和a。

输出格式:
一个整数,为最大价值总和是多少。

输入样例:

4 20
9 3 3
9 5 1
4 9 2
1 8 3

输出样例:

47

暴力解法

我们发现多重背包和零一背包十分相像,只不过多重背包中,每个物品可以选0 ~ a个。这会让我们想到,直接把第i个物品,拆成a[i]个重量都为w[i],价格也都为v[i]的物品不就行了吗,接下来一个经典的零一背包就可以搞定了。

:如果你还没学过零一背包,可以去此网址先学习一下https://blog.csdn.net/SkeletonKing233/article/details/94893608

(PS:NR是指物品个数的上限,MR是指重量的上限)

# include <cstdio>
# include <iostream>
# include <algorithm>
# include <cmath>
# include <cstring>

using namespace std;

# define FOR(i, a, b) for(int i = a; i <= b; i++)
# define _FOR(i, a, b) for(int i = a; i >= b; i--)

const int NR = 100000, MR = 100000000;

int n, m;
int v[NR + 10], w[NR + 10];
int dp[MR + 10];

int main()
{
	int T;
	cin >> T >> m;
	FOR(i, 1, T){
		int x, y, z;
		cin >> x >> y >> z;
		FOR(j, 1, z){
			w[++n] = x;
			v[n] = y;
		}
	}
	FOR(i, 1, n)
		_FOR(j, m, w[i])
			dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
	cout << dp[m] << endl;
	return 0;
}

优化解法

如果我们按上面的写法,我们会发现时间和空间的占用太大了,这该怎么办呢?让我们来一起分析一下。

一个物品i,原本只需讨论个数为0 ~ a[i],这(a[i] + 1)种情况。可现在我们拆成了a[i]完全一样的物品,按照零一背包dp,就分析了2 ^ a[i]种情况,而其中许多情况是重复的(例如选第一个、不选第二个和选第二个、不选第一个是一样的)。这样就大大消耗了空间和时间。

那么现在我们就该想了,如何才能让方案尽量少的重复呢?拆成这么多个物品必要吗?这时我们又会想如何用几个整数表示出所有的0 ~ a[i]个数呢?我们发现如果拿{1, 2, 4, 8, ………,2 ^ p,a[i] - 前面所有的数}就可以表示出所有的从0 ~ a[i]的整数了!(5 = 4 + 1, 19 = 16 + 2 + 1 ……),那么我们就只用把一个物品,拆成p或(p + 1)个物品就行了。这样就大大减少了空间与时间的消耗。

# include <cstdio>
# include <iostream>
# include <cstring>
# include <algorithm>
# include <cmath>

using namespace std;

# define FOR(i, a, b) for(int i = a; i <= b; i++)
# define _FOR(i, a, b) for(int i = a; i >= b; i--)

const int NR = 100000, MR = 100000000;

int n, m;
int v[NR + 10], w[NR + 10];
int dp[MR + 10];

int main()
{
	int T;
	cin >> T >> m;
	while(T--){
		int x, y, z;
		cin >> x >> y >> z;
		for(int i = 1; i <= z; i *= 2){
			w[++n] = i * x;
			v[n] = i * y;
			z -= i;
		}
		if(z != 0){
			w[++n] = z * x;
			v[n] = z * y;
		}
	}
	FOR(i, 1, n)
		_FOR(j, m, w[i])
			dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
	cout << dp[m] << endl;
	return 0;
}

God Bless You For Ever!

发布了33 篇原创文章 · 获赞 47 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/SkeletonKing233/article/details/99436083