背包九讲(超详细 :算法分析 + 问题分析 + 代码分析)

这篇具有很好参考价值的文章主要介绍了背包九讲(超详细 :算法分析 + 问题分析 + 代码分析)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 01背包问题

特点:每个物品只能用一次,只能是选择或者不选择

题目链接
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例:

4 5
1 2
2 4
3 4
4 5

输出样例:

8

f [ i ][ j ] 表示只看前 i 个物品,总体积是 j 的情况下,总价值最大是多少

result = max { f [ n ][ 0 ~ v ] }

f [ i ][ j ] :
1 . 不选第 i 件物品,f [ i ][ j ] = f [ i - 1][ j ];
2 . 选第 i 件物品,f [ i ][ j ] = f [ i - 1][ j - v[ i ] ];
f [ i ][ j ] = max { 1, 2 }

f [ 0 ][ 0 ] = 0;

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;

int f[N][N];
int v[N], w[N];
int n, m;

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

优化为 1 维的 : f [ i ][ j ] => f [ j ]
f [ j ] 总体是 j 的情况下,总价值最大是多少

result = max { f[ 0 ~ v ] }; => result = f [ m ]

f [ j ]:
1 . 不选第 i 个物品,f [ j ] = f [ j ];
2 . 选第 i 个物品,f [ j ] = f [ j - v [ i ] ];(注意:这个时候的循环要从大到小循环了)

for(int i = 1; i <= n; i ++ )
	for(int j = m; j >= v[i]; j -- )
		f[j] = max(f[j], f[j - v[i]] + w[i]);

f [ j ] = max {1, 2};

result = f [ m ]
1 . 不超过最大体积 m 的最大价值:f [ 0 ~ v ] = 0;
2 . 体积恰好为 m 的最大价值:f [ 0 ] = 0,f [ 1 ~ v ] = -∞

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;

int f[N];
int v[N], w[N];
int n, m;

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 = m; j >= v[i]; j -- )
			f[j] = max(f[j], f[j - v[i]] + w[i]);
	cout << f[m];
	return 0;
}

2. 完全背包问题

特点:每种物品有无限个,可以选择 0~n 个

题目链接

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。

第 i 种物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例:

4 5
1 2
2 4
3 4
4 5

输出样例:

10

f [ i ] 表示总体积是 i 的情况下,最大价值是多少

result = max { f [ 0 ~ m ] };

f [ i ] :

	for(int i = 1; i <= n; i ++ )
		for(int j = v[i]; j <= m; j ++ )
			f[j] = max([j], [j - v[i]] + w[i]);

result = f [ m ]
1 . 不超过最大体积 m 的最大价值:f [ 0 ~ v ] = 0;
2 . 体积恰好为 m 的最大价值:f [ 0 ] = 0,f [ 1 ~ v ] = -∞

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;

int f[N];
int v[N], w[N];
int n, m;

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 = v[i]; j <= m; j ++ )
			f[j] = max(f[j], f[j - v[i]] + w[i]);
	cout << f[m];
	return 0;
}

3. 多重背包问题

特点:每件物品有 s 件,对于每件物品可以选择 0 ~ s 件

题目链接

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤100
0<vi,wi,si≤100

输入样例:

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

f [ i ] 表示总体积是 i 的情况下,最大值是多少

f [ i ] :

for(int i = 1; i <= n; i ++ )
	for(int j = m; j >= v[i]; j -- )
		f[j] = max(f[j], f[j - 1 * v[i]] + 1 * w[i], f[j - 2 * v[i]] + 2 * w[i] ... )

result = f [ m ]
1 . 不超过最大体积 m 的最大价值:f [ 0 ~ v ] = 0;
2 . 体积恰好为 m 的最大价值:f [ 0 ] = 0,f [ 1 ~ v ] = -∞

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;

int f[N];
int v[N], w[N], s[N];
int n, m;

int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i] >> s[i];
	for(int i = 1; i <= n; i ++ )
		for(int j = m; j >= v[i]; j -- )
			for(int k = 0; k <= s[i] && k * v[i] <= j; k ++ )
				f[j] = max(f[j], f[j - k * v[i]] + k * w[i]);
	cout << f[m];
	return 0;
}

题目链接

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N≤1000
0<V≤2000
0<vi,wi,si≤2000

提示:

本题考查多重背包的二进制优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

二进制优化

举个栗子:
7 -> 我们可以分解为 7 个 1 相加,但是这样分解后,时间复杂度并没有降低
-> 我们还可以分解成 1 、 2 、 4 相加,这样分解后,时间复杂度很明显就降低了,那么1 、 2 、 4 是怎么来的呢?
20 = 1;
21 = 2;
22 = 4;
其实就是 log27 向上取整得到 2 ,所以是 20 - 22 之间的数字
这就是乘法原理,利用1 、 2 、 4,我么就可以将 0 - 7 之间的所有数字表示出来了
如:
0 = 0
1 = 1
2 = 2
3 = 1 + 2
4 = 4
5 = 1 + 4
6 = 2 + 4
7 = 1 + 2 + 4
但是当 7 变成 10 呢,继续利用 log210 向上取整,得到的 3 ,也就是 1 、 2 、 4 、 8,但是此时表示的最大数值为 15 ,并不是 10 ,所以,我只选择 1 、 2 、 4,剩余的数字直接用 10 - 1 - 2 - 4 - 8 = 3 表示
1 、 2 、 4 可以是表示 0 - 7 之间的全部数字,
那么 1 、 2 、4 、 3 就可以表示 0 - 10 之间的全部数字

利用这个方法,我们就可以把数据范围为 2000 的数量变成了 log22000 ≈ 11 ,这样我们在继续实现多重背包就可以了

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;

int f[N];
int v[N], w[N], s[N];
int n, m;

struct Good{
	int v, w;
};

int main()
{
	vector<Good> good;
	cin >> n >> m;
	for(int i = 0; i < n; i ++ ) cin >> v[i] >> w[i] >> s[i];
	for(int i = 0; i < n; i ++ )
	{
		// 二进制处理 
		for(int j = 1; j <= s[i]; j *= 2 )
		{
			s[i] -= j;
			good.push_back({v[i] * j, w[i] * j});
		}
		if(s[i] >= 0) good.push_back({v[i] * s[i], w[i] * s[i]});
	}
	// 多重背包
	for(auto goods : good)
		for(int j = m; j >= goods.v; j -- )
			f[j] = max(f[j], f[j - goods.v] + goods.w);
	cout << f[m] << endl;
	return 0;
}

题目链接

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N≤1000
0<V≤20000
0<vi,wi,si≤20000

提示:

本题考查多重背包的单调队列优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

基本的多重背包问题状态转移方程:
f [ j ] = max ( f [ j ] , f [ j - k * v ] + k * w );
f [ j ] = max ( f [ j ] , f [ j - v ] + w , f [ j - 2 * v ] + 2 * w , f [ j - 3 * v ] + 3 * w … f [ j - s * v ] + s * w )
f [ j - v ] = max ( f [ j - v ] , f [ j - 2 * v ] + w , f [ j - 3 * v ] + 2 * w , f [ j - s * v ] + ( s - 1 ) * w , f [ j - ( s + 1 ) * v ] + s * w )
f [ j - 2 * v ] = …
f [ j - 3 * v ] = …
从上面的 f [ j ] 和 f [ j - v ] 我们可以看出来,相同的部分为:
f [ j - v ] + w , f [ j - 2 * v ] + 2 * w , f [ j - 3 * v ] + 3 * w … f [ j - s * v ] + s * w
f [ j - v ] , f [ j - 2 * v ] + w , f [ j - 3 * v ] + 2 * w , f [ j - s * v ] + ( s - 1 ) * w
因此我们只需要维护 f [ j - v ] 到 f [ j - s * v ] 这个区间中的最大值就可以,而维护一个区间的最大值我们就可以使用单调队列来进行维护

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=20010;

int n, m;
int v[N], w[N], s[N];
int f[N], g[N], q[N];

int main()
{
    cin >> n >> m;
    for(int i = 0; i < n; i ++ ) cin >> v[i] >> w[i] >> s[i];
	for(int i = 0; i < n; i ++ )
	{
		// 滚动数组 
		memcpy(g, f, sizeof f);
		for(int j = 0; j < v[i]; j ++ )
		{
			int hh = 0, tt = -1;
			for(int k = j; k<= m; k += v[i])
			{
				if(hh <= tt && q[hh] < k - s[i] * v[i]) hh ++ ;
				while(hh <= tt && g[q[tt]] - (q[tt] - j) / v[i] * w[i] <= g[k] - (k - j) / v[i] * w[i]) tt -- ;
				q[ ++ tt] = k;
				f[k] = g[q[hh]] + (k - q[hh]) / v[i] * w[i]; 
			}
		} 
	} 
	cout << f[m] << endl;
	return 0;
}

4. 混合背包问题

特点:包含01背包、完全背包、多重背包三种背包问题

题目链接

有 N 种物品和一个容量是 V 的背包。

物品一共有三类:

第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si 次(多重背包);
每种体积是 vi,价值是 wi

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

si=−1 表示第 i 种物品只能用1次;
si=0 表示第 i 种物品可以用无限次;
si>0 表示第 i 种物品可以使用 si 次;
输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000
−1≤si≤1000

输入样例

4 5
1 2 -1
2 4 1
3 4 0
4 5 2

输出样例:

8

对于多重背包,我们利用二进制优化后,也就是二进制分组后,对于每一组都相当于01背包,所以在标记背包种类的时候,要标记成01背包

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;

int n, m;
int v[N], w[N], s[N];
int f[N];

struct Good{
	int kind;
	int v, w;
};

vector<Good> good;

int main()
{
    cin >> n >> m;
    for(int i = 0; i < n; i ++ ) cin >> v[i] >> w[i] >> s[i];
    for(int i = 0; i < n; i ++ )
    {
    	if(s[i] == -1) good.push_back({-1, v[i], w[i]});
    	else if(s[i] == 0) good.push_back({0, v[i], w[i]});
    	// 多重背包二进制优化(分组)之后,对于每一组都相当于是01背包 
		else
    	{
    		for(int k = 1; k <= s[i]; k *= 2)
    		{
    			s[i] -= k;
    			good.push_back({-1, v[i] * k, w[i] * k});
			}
			if(s[i] > 0) good.push_back({-1, v[i] * s[i], w[i] * s[i]});
		}
	}
	// 对于01背包和完全背包分两种情况状态转移即可 
	for(auto goods : good)
	{
		// 01背包 
		if(goods.kind == -1)
		{
			for(int j = m; j >= goods.v; j -- )
				f[j] = max(f[j], f[j - goods.v] + goods.w);
		}
		else
		{
			for(int j = goods.v; j <= m; j ++ )
				f[j] = max(f[j], f[j - goods.v] + goods.w); 
		} 
	} 
	cout << f[m] << endl;
	return 0;
}

5. 二维费用的背包问题

特点:一般的背包问题,只有一个容量的限制,对于二维背包问题,除了容量的限制还有另一个限制,

题目链接

有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。

每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi

求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。
输出最大价值。

输入格式
第一行三个整数,N,V,M,用空格隔开,分别表示物品件数、背包容积和背包可承受的最大重量。

接下来有 N 行,每行三个整数 vi,mi,wi,用空格隔开,分别表示第 i 件物品的体积、重量和价值。

输出格式
输出一个整数,表示最大价值。

数据范围

0<N≤1000
0<V,M≤100
0<vi,mi≤100
0<wi≤1000

输入样例

4 5 6
1 2 3
2 4 4
3 4 5
4 5 6

输出样例:

8

f [ i ][ j ] 表示总体积是 i ,总重量是 j 的情况下,最大价值是多少

同时对于状态转移的时候,两层 for 循环,一层是体积限制,一层是重量限制

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;

int a, b, c;
int v[N], t[N], w[N];
int f[N][N];

int main()
{
    cin >> a >> b >> c;
    for(int i = 0; i < a; i ++ ) cin >> v[i] >> t[i] >> w[i];
    for(int i = 0; i < a; i ++ )
    	for(int j = b; j >= v[i]; j -- )
    		for(int k = c; k >= t[i]; k -- )
    			f[j][k] = max(f[j][k], f[j - v[i]][k - t[i]] + w[i]);
	cout << f[b][c] << endl;
	return 0;
}

6. 分组背包问题

特点:每一组中有多个物品,但是同组内的物品最多只能选择一个

题目链接

有 N 组物品和一个容量是 V 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。

接下来有 N 组数据:

每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
输出格式
输出一个整数,表示最大价值。

数据范围

0<N,V≤100
0<Si≤100
0<vij,wij≤100

输入样例

3 5
2
1 2
2 4
1
3 4
1
4 5

输出样例:

8

对于不同组之间来说就相当于普通的背包问题,但是在同一组中,就只能选择一个物品,也就是说,选择了第一组中的第一个物品,那么第一组中的其他物品就不能够再选择了,相反,如果没有选择第一组中的第一个物品,那么就要选择,第一组中的其他物品,当然在选择的时候,相当于是01背包,所以在选择物品的时候,要进行判断
j >= v [ k ]
v [ k ] :某一组中的某一个物品的体积

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;

int n, m, s;
int v[N], w[N];
int f[N];

int main()
{
    cin >> n >> m;
    for(int i = 0; i < n; i ++ )
    {
    	int s;
    	cin >> s;
    	for(int j = 0; j < s; j ++ ) cin >> v[j] >> w[j];
    	for(int j = m; j >= 0; j -- )
    		for(int k = 0; k < s; k ++ )
    			if(j >= v[k])
	    			f[j] = max(f[j], f[j - v[k]] + w[k]);
	}
	cout << f[m] << endl;
	return 0;
}

7. 背包问题求方案数

特点:在背包问题下,求出最优选法的方案数

题目链接

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示 方案数 模 109+7 的结果。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

2

f [ j ] 表示体积恰好是 j 的情况下,最大价值是多少
g [ j ] 表示体积恰好是 j 的情况下,方案数是多少

这个地方的 f [] 数组的含义与前面的背包问题不一样,所以在这里的初始化,也与前面不同,这个地方在前面的背包问题上有所提到
f [ 0 - N ] = -∞
f [ 0 ] = 0,g [ 0 ] = 1

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, m;
int v[N], w[N];
int f[N], g[N];
int main()
{
	cin >> n >> m;
	g[0] = 1;
	for(int i = 1; i <= m; i ++ ) f[i] = -INF;
	for(int i = 0; i < n; i ++ ) cin >> v[i] >> w[i];
	for(int i = 0; i < n; i ++ )
		for(int j = m; j >= v[i]; j -- )
		{
			int t = max(f[j], f[j - v[i]] + w[i]);
			int s = 0;
			if(t == f[j]) s += g[j];
			if(t == f[j - v[i]] + w[i]) s += g[j - v[i]];
			if(s >= mod) s -= mod;
			f[j] = t;
			g[j] = s;
		}
	int maxx = 0;
	for(int i = 0; i <= m; i ++ ) maxx = max(maxx, f[i]);
	int ans = 0;
	for(int i = 0; i <= m; i ++ )
		if(maxx == f[i])
		{
			ans += g[i];
			if(ans >= mod) ans -= mod;
		}
	cout << ans << endl;
	return 0;
}

8. 求背包问题的方案

特点:在普通背包的基础下,输出选择方案,比如选择物品1 、 3 、 4是最优解,那么就输出 1 、 3 、 4

题目链接

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出 字典序最小的方案。这里的字典序是指:所选物品的编号所构成的序列。物品的编号范围是 1…N。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一行,包含若干个用空格隔开的整数,表示最优解中所选物品的编号序列,且该编号序列的字典序最小。

物品编号范围是 1…N。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 6

输出样例:

1 4

我们要输出所选的方案数,首先我们按照普通背包的问题求解后,我们会得到 f [] 数组,因此如果我们要求出选择了哪一些物品,我们就可以对 f [] 数组进行反推,
如果 f [ i ][ j ] == f [ i ][ j ] ,说明选择了第 i 个物品
如果 f [ i ][ j ] == f [ i - 1 ][ j - v[ i ] ] ,说明选择了第 i - 1 个物品
这个题目为了解唯一,他的答案方案数是字典序最小的方案,因此我们可以按照贪心的思想去做,如果能选择第 i 个物品,那么我们就直接选第 i 个物品,而不选第 i + 1 个物品(如果选择第 i 个和选择第 i + 1 个物品所得到的最大价值相同的话)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n, m;
int v[N], w[N];
int f[N][N];
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
	for(int i = n; i >= 1; i -- )
		for(int j = 0; j <= m; j ++ )
		{
			f[i][j] = f[i + 1][j];
			if(j >= v[i]) f[i][j] = max(f[i][j], f[i + 1][j - v[i]] + w[i]);
		}
	int vol = m;
	for(int i = 1; i <= n; i ++ )
		if(vol - v[i] >= 0 && f[i][vol] == f[i + 1][vol - v[i]] + w[i])
		{
			cout << i << ' ';
			vol -= v[i];
		}	
	return 0;
}

9. 有依赖的背包问题

特点:N个物品,V容量的背包,物品之间有依赖关系,且依赖关系可以构成一棵树,如果选择某一个物品那么就必须先选择这个物品的父亲物品

题目链接

有 N 个物品和一个容量是 V 的背包。

物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。

如下图所示:
背包九讲(超详细 :算法分析 + 问题分析 + 代码分析)

如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。

每件物品的编号是 i,体积是 vi,价值是 wi,依赖的父节点编号是 pi。物品的下标范围是 1…N。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品个数和背包容量。

接下来有 N 行数据,每行数据表示一个物品。
第 i 行有三个整数 vi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 pi=−1,表示根节点。 数据保证所有物品构成一棵树。

输出格式
输出一个整数,表示最大价值。

数据范围

1≤N,V≤100
1≤vi,wi≤100

父节点编号范围:

  • 内部结点:1≤pi≤N;
  • 根节点 pi=−1;

输入样例

5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2

输出样例:

11

f [ i ][ j ] 表示选择结点 i (在这里也就是根节点),总体积是 j 时,最大价值是多少

当我们选择根节点后,对于根节点的每一棵子树,都相当于对根节点做分组背包了,每一个根节点相当于一个组,对于组中的节点分为选择或者不能选择文章来源地址https://www.toymoban.com/news/detail-401074.html

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<sstream>

using namespace std;
typedef long long ll;
const int N=1010;

int n, m;
int v[N], w[N];
int f[N][N];
int h[N], e[N], ne[N], idx;

void add(int a, int b)
{
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx ++ ;
}

void dfs(int u)
{
	for(int i = h[u]; i != -1; i = ne[i])
	{
		int son = e[i];
		dfs(son);
		for(int j = m - v[u]; j >= 0; j --)
			for(int k = 0; k <= j; k ++ )
				f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);
	}
	// 将根节点 u 加上 
	for(int i = m; i >= v[u]; i -- )
		f[u][i] = f[u][i - v[u]] + w[u];
	// 如果选择了子节点后,还没有选择到最后的 root ,那么就不能选择当前结点及其根节点了 
	for(int i = 0; i < v[u]; i ++ )
		f[u][i] = 0;
}

int main()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);
	int root;
	for(int i = 1; i <= n; i ++ )
	{
		int p;
		cin >> v[i] >> w[i] >> p;
		if(p == -1) root = i;
		else add(p, i);
	}  
	dfs(root);
	cout << f[root][m] << endl; 
	return 0;
}

到了这里,关于背包九讲(超详细 :算法分析 + 问题分析 + 代码分析)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态规划:0-1背包、完全背包问题 | 详细原理解释 | 代码及优化(C++)

    目录 01背包 问题描述: 简单描述就是: 解析: 递推公式: dp数组的初始化: 遍历顺序: 图解: 实现代码: dp数组初始化: 遍历: 优化: 原理: 递推公式: 遍历顺序: 实现代码: 初始化: 遍历: 完全背包 问题描述: 解析: 实现代码:         01背包是在M件物品

    2024年02月11日
    浏览(37)
  • 算法分析与设计——动态规划求解01背包问题

    假设有四个物品,如下图,背包总容量为8,求背包装入哪些物品时累计的价值最多。 我们使用动态规划来解决这个问题,首先使用一个表格来模拟整个算法的过程。 表格中的信息表示 指定情况下能产生的最大价值 。例如, (4, 8)表示在背包容量为8的情况下,前四个物品的最

    2024年02月04日
    浏览(70)
  • 算法设计与分析实验二:动态规划法求解TSP问题和01背包问题

    【实验内容】 (1)tsp问题:利用动态规划算法编程求解TSP问题,并进行时间复杂性分析。 输入:n个城市,权值,任选一个城市出发; 输出:以表格形式输出结果,并给出向量解和最短路径长度。 (2)01背包问题:利用动态规划算法编程求解0-1背包问题,并进行时间复杂性分

    2024年02月03日
    浏览(58)
  • 【算法】算法学习七:动态规划 | 背包问题 | 最长公共子串(含源代码)

    背包问题是一种经典的组合优化问题,通常有两个版本:0-1背包问题和无限背包问题。 0-1背包问题是指给定一个背包容量和一组物品,每个物品有自己的重量和价值,要求在不超过背包容量的情况下,选择一些物品放入背包,使得物品的总价值最大化。每个物品只能选择放入

    2024年02月09日
    浏览(48)
  • 【算法设计与分析基础(第三版)习题答案】8.2 背包问题和记忆功能

    a. 对于下列背包问题的实例,应用自底向上动态规划算法求解。 b. a 中的实例有多少不同的最优子集 c. 一般来说,如何从动态规划算法所生成的表中判断出背包问题的实例是不是具有不止一个最优子集? 承重量 W = 6 物品 重量 价值 1 3 25 2 2 20 3 1 15 4 4 40 5 5 50 题目中要求使用

    2023年04月08日
    浏览(41)
  • 湘潭大学 算法设计与分析实验 回溯 动态规划 贪心 模拟退火解决背包问题

    https://download.csdn.net/download/SQ_ZengYX/88620871 测试用例

    2024年02月02日
    浏览(62)
  • 一本通 1267:【例9.11】01背包问题(详细代码+严谨思路+清晰图片) C++

    经典01背包问题 这里给你  3  种方法!! 目录 DFS 思路: 代码: DFS+记忆化 思路: 代码: 动态规划 思路: 代码: 时间复杂度 :O(2^n) DFS求出所有选法,再用ans记录价格最大值 由于此题数据量较小(其实2^30=1073741824,这种做法是过不了的,是题目数据比较水^_^) 如果在考试遇

    2024年02月13日
    浏览(35)
  • 背包问题之0-1背包算法详解

    有5件物品和1个背包,背包最多只能装下8公斤的物品。怎样选择物品,使得背包能装下并且得到的价值最大。物品的重量和价值如下所示: 物品1: 6公斤    价值48元 物品2: 1公斤   价值7元 物品3: 5公斤   价值40元 物品4: 2公斤    价值12元 物品5: 1公斤   价值8元 可以先考虑

    2023年04月09日
    浏览(45)
  • 【算法宇宙——在故事中学算法】背包dp之完全背包问题

    学习者不灵丝相传,而自杖明月相反,子来此事却无得失。 尽管计算机是门严谨的学科,但正因为严谨,所以要有趣味才能看得下去。在笔者的前几篇算法类文章中,都采用了以小故事作为引入的方式来介绍算法,但在回看的时候发现学术味还是太浓了,完全没有想看下去的

    2023年04月20日
    浏览(53)
  • 算法系列--动态规划--背包问题(3)--完全背包介绍

    💕\\\"Su7\\\"💕 作者:Lvzi 文章主要内容:算法系列–动态规划–背包问题(3)–完全背包介绍 大家好,今天为大家带来的是 算法系列--动态规划--背包问题(3)--完全背包介绍 链接: 完全背包 可以发现完全背包问题和01背包问题还是特比相似的 分析: 完全背包问题 是 01背包问题 的推广

    2024年04月25日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包