寒假训练营 第十六节 动态规划(三)总结

文章介绍了使用动态规划解决分组背包和多重背包问题的算法。分组背包要求每组最多选一件物品,而多重背包允许每种物品有多件。通过建立状态转移方程,分别计算每种情况下能获得的最大价值。这两种问题都可以通过枚举物品和容量来优化选择,以达到背包容量限制下的最大价值目标。
摘要由CSDN通过智能技术生成

分组背包:

	分组背包 
			n , m
			n 物品组
			m 背包体积
			每组物品里面, 有多种物品,(每种物品只有一件)
			给出每组物品里, 每件物品的价值, 体积 
			
			求 从 n 组物品中选, 每组物品最 多选一件物品, 也可以不选
				总体积不超过 m , 总价值最大
				
	f[i, j]
				f[i, j] = k; 
			1. 集合 
					表示: n 组物品中选, 每组物品最 多选一件物品, 也可以不选
						   总体积不超过 m 的所有选法的集合
						(属性, 数)存的数是这个集合中的某一个方案, 这个方案总价值最大
						max(价值) 
			2. 计算集合
					f[i, j] ---> 包含的子集, 只要找 到所有子集取 Max 
					在这个题里面, 每个子集就是一种方案 
					max(1, 2, 3, 4, ...i);
					
					f[i, j]
					不选 第 i 组的物品
						f[i, j] = max(f[i, j], f[i - 1, j])
						 
					选 第 i 组的物品
						选第 i 组的哪一个物品 
						     k: 第 i 组 物品中第 k 个物品 
						f[i, j] = max(f[i - 1, j - vik] + wik, f[i, j])    
f[i,j] = max(f[i-1,j], f[i-1,j-vi1]+wi1, f[i-1,j-vi2]+wi2...f[i-1,j-vik]+wik)
f[i,j-vi1] = max(f[i-1,j-vi1],f[i-1,j-2vi1]+wi1,f[i-1,j-vi1-vi2]+wi2...f[i-1,j-vi1-vik]+wik) 
			
			3. 边界: f[0, j] = 0;
			4. O(n^3)
1、题目详情:洛谷U280369 分组背包问题

有 N 组物品和一个容量是 V 的背包。每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。输出最大价值。

#include<bits/stdc++.h>
using namespace std;
const int N = 110; 
int f[N][N];
int s[N], v[N][N], w[N][N];
int main(){
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i ++ ){
		cin >> s[i];
		for (int j = 1; j <= s[i]; j ++ )
			cin >> v[i][j] >> w[i][j]; 
	}
	for (int i = 1; i <= n; i ++ )   // 物品组 
		for (int j = 1; j <= m; j ++ ){  // 体积
			f[i][j] = f[i - 1][j];
			for (int k = 1; k <= s[i]; k ++ )  // 每组物品中的每个物品 
				if (v[i][k] <= j)
					f[i][j] = max(f[i - 1][j - v[i][k]] + w[i][k], f[i][j]); 
		} 	
	cout << f[n][m] << endl;
	return 0;
}

思路: 首先判断一个分组当中的一件物品,同01背包一样,此物品存在两种状态,取与不取,若取此物品,则继续判断下一组的第一件物品,若不取此物品,则继续判断本组下一件物品,若该物品为本组最后一件物品,则判断下一组。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}
应当先枚举v,再枚举每一个组内物品。(是为了防止一组内的物品被重复选择)

多重背包:

	多重背包
		n	n 种物品, 每种物品有多件, 每种物品的体积和价值不变 
		m   背包体积 
		
	f[i, j]
			1. 集合
				f[i, j]表示: 从前 i 种物品中选(每种物品有多件), 所选物品总体积不超过j 的所有选法的集合
						存的是集合中的某一方案, 这个方案价值max 
			2. 计算集合
						f[i, j]	
						枚举第 i 种物品选 了 几个
						(0i, 1i, 2i, 3i, ... ki)
						f[i, j] = max(f[i - 1, j - k*vi] + k*wi, f[i, j])
f[i,j]=max(f[i-1,j],f[i-1,j-vi]+wi,f[i-1,j-2vi]+2wi...f[i-1,si*vi]+si  *wi]);
f[i,j-vi]=max(f[i-1,j-vi],f[i-1,j-2vi]+ wi...f[i-1,j-(si-1)vi-vi)+(si-1)wi,f[i-1,j-(si+1)vi]+si*wi)	
			3. 边界(初始化)
						f[0, j] = 0;
			f[i,j]
			4. 时间	 O(n*m*si)
2、题目详情:洛谷U280382 多重背包问题

有 N 种物品和一个容量是 V 的背包。第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。输出最大价值。

基本思路: 比如第 i 件物品有 s 个,我可以把相同种类的物品的进行合并。那么我们可以把第 i 件的 s 个物品转换为s种体积各不相同的物品,然后在用01背包的思想,求出最优解!

#include<bits/stdc++.h>
using namespace std;
const int N = 8 * 100, M = 1e4;
int f[N][M];
int v[N], w[N];
int main(){
	int n, m;
	cin >> n >> m;
	int cnt = 0;
	for (int i = 1; i <= n; i ++ ){
		int a, b, s;
		cin >> a >> b >> s;
		int k = 0;  // 2^k
		while (s > k){
			if (k == 0) k ++ ;
			cnt ++ ;
			v[cnt] = k * a;
			w[cnt] = k * b;
			s -= k;
			k *= 2;
		}
		if (s){
			cnt ++ ;
			v[cnt] = s * a;
			w[cnt] = s * b;
		}
	}
	n = cnt;
	for (int i = 1; i <= n; i ++ )
		for (int j = 1; 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]);
		}
	cout << f[n][m] << endl;
	return 0;
}

多重背包也是一种基本的背包问题模型,其基本特点是:每种物品有一个固定的装入次数上限。
多重背包问题的一般描述为:有N个物品,第i个物品的重量与价值分别为W[i]与P[i]且第i种物品最多有C[i] 件。背包容量为V,试问在每个物品不超过其上限的件数(物品必须保持完整)的情况下,如何让背包装入的物品具有更大的价值总和。
一般解题思路为:设f[i][j] 表示从编号1~i的物品中挑选任意数量的任意物品放入容量为j的背包中得到的最大价值,那么有 f[i][j]=max { f[i−1][j−k∗w[i]]+k∗v[i] ∣0≤k≤p[i]&k∗w[i]≤j }。
多重背包我们其实可以看成为01背包和完全背包的组合。​​​​​​​也可以把多重背包问题只转换成01背包问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值