背包问题变式

这篇博客探讨了如何使用动态规划解决两类背包问题:一是求体积不超过限制的最大价值,二是寻找总价格不超过预算的最小重量机器设计。在第一类问题中,每组物品必须恰好选取一个,并要求输出字典序最小的选取方案。在第二类问题中,给出了每个部件的不同供应商及其重量和价格,目标是找到总价格不超过预算的最小重量设计。博主提供了详细的算法实现和样例解析,强调了字典序最小方案的计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.有n组物品。每组物品里面必须恰好选1个。求体积不超过m的最大价值。

题解:
跟分组背包的类似,第i-1的状态只有选择了物品,才有价值否则为0。把 dpi,j=dpi−1,jdpi,j=dpi-1,jdpi,j=dpi1,j 这个去掉。让转移必须由 i 层选择了物品才能从i-1走过来
如果第i层没有选任何物品,则第i层的状态就会出现价值为0的断层,后面的就不会更新了
注意i=1的特别情况即可

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int f[N][N],n,c,v[N],w[N];
int main()
{
	cin>>n>>c;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<2;j++)cin>>v[j]>>w[j];
		for(int j=c;j>=0;j--)
		{
			for(int s=0;s<2;s++)
			{
				if(j>=w[s])
				{
					if(f[i-1][j-w[s]]>0)f[i][j]=max(f[i-1][j-w[s]]+v[s],f[i][j]);
					else if(i==1)f[i][j]=max(f[i-1][j-w[s]]+v[s],f[i][j]);
				}
				
			}
		}
	}
	cout<<f[n][c];
	return 0;
}

2.最小重量机器设计问题(分组背包求方案)

设某一机器由n个部件组成,每一种部件都可以从m个不同的供应商处购得。设w​ij是从供应商j 处购得的部件i的重量,c​ij是相应的价格。 试设计一个算法,给出总价格不超过d的最小重量机器设计。

输入格式:
第一行有3 个正整数n ,m和d, 0<n<30, 0<m<30, 接下来的2n 行,每行n个数。前n行是c,后n行是w。

输出格式:
输出计算出的最小重量,以及每个部件的供应商

输入样例:
3 3 4
1 2 3
3 2 1
2 2 2
1 2 3
3 2 1
2 2 2
输出样例:
在这里给出相应的输出。例如:

4
1 3 1
·······················································································
题意:给你n组背包,每组m个物品。每组必须只能恰好选一个。求体积不超过d的最小价值。并输出字典序最小方案

题解:因为要求最小价值。所以初始化成无穷大。因为要求字典序最小方案。所以定义f[i][j]f[i][j]f[i][j]表示 i−ni-nin物品体积不超过j的最小价值。计算f[i][j]f[i][j]f[i][j]的过程与1类似,从n往1计算。
这样最后才能从1-n枚举取哪个物品,保证是字典序最小。

若最后从n开始枚举选取的物品。会被这组数据卡掉。正解是1.2,错解输出2.1,不是字典序最小了。

2 2 10
6 4 
6 4 
4 4 
4 4 

#include<bits/stdc++.h>
using namespace std;
const int N=35,M=10005;
#define inf 0x3f3f3f3f
int f[N][M],n,m,d,v[N][N],w[N][N];
int main()
{
	cin>>n>>m>>d;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)cin>>w[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)cin>>v[i][j];
	memset(f,0x3f,sizeof f);
	for(int i=0;i<=d;i++)f[n+1][i]=0;
	for(int i=n;i>=1;i--)
	{
		for(int j=d;j>=0;j--)
		{
			for(int s=1;s<=m;s++)
			{
				if(j>=w[i][s])
				{
					if(f[i+1][j-w[i][s]]!=inf)	f[i][j]=min(f[i+1][j-w[i][s]]+v[i][s],f[i][j]);
					else if(i==1)f[i][j]=min(f[i+1][j-w[i][s]]+v[i][s],f[i][j]);
				}				
			}			
		}
	}
	vector<int>ans;
	int vol=d;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(vol>=w[i][j]&&f[i][vol]==f[i+1][vol-w[i][j]]+v[i][j])
			{
				vol-=w[i][j];
				ans.push_back(j);
				break;//每组只取一个,取到了就break
			}
		}
	}
	cout<<f[1][d]<<endl;
	for(auto i:ans)printf("%d ",i);
	return 0;
}
### 01背包问题及解法 #### 01背包问题的基本定义 01背包问题是一种经典的动态规划问题,其特点是每种物品只能使用一次。给定一个容量为 `W` 的背包和 `N` 个物品,每个物品有两个属性:重量 `w[i]` 和价值 `v[i]`,目标是选择物品入背包中,使得总重量不超过 `W`,并且总价值最大。 #### 一:01背包问题的二维限制 在某些情况下,背包问题的约束可能不仅仅是单一的容量,而是存在两个维度的限制。例如,在引用[1]中提到的问题,每个物品需要同时消耗两种资源(如0和1的数量),这可以被建模为一种二维01背包问题。目标是在给定的资源限制下最大化子集的数量。 ##### 解法 定义 `dp[i][j]` 表示在有 `i` 个0和 `j` 个1 的情况下,能够装下的最大子集数量。对于每个物品,统计其所需的0和1的数量(`cnt0` 和 `cnt1`),然后从后向前更新 `dp` 数组,以避免重复计算。 ```cpp class Solution { public: int findMaxForm(vector<string>& strs, int m, int n) { int dp[105][105] = {0}; int len = strs.size(); for (int k = 0; k < len; ++k) { int cnt0 = 0, cnt1 = 0; for (char c : strs[k]) { if (c == '0') ++cnt0; else ++cnt1; } for (int i = m; i >= cnt0; --i) { for (int j = n; j >= cnt1; --j) { dp[i][j] = max(dp[i - cnt0][j - cnt1] + 1, dp[i][j]); } } } return dp[m][n]; } }; ``` #### 二:完全背包问题 完全背包问题与01背包的区别在于,每个物品可以选择多次。这种可以通过调整内循环的方向来实现。在01背包中,内循环是从后向前的,而在完全背包中,内循环是正向的,因为物品可以无限次选择。 ##### 解法 定义 `dp[j]` 表示容量为 `j` 时的最大价值。对于每个物品,遍历容量从 `w[i]` 到 `W`,并更新 `dp[j]`。 ```cpp 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]); } } ``` #### 三:多重背包问题 多重背包问题中,每个物品有一个固定的使用次数限制。这种可以通过将每个物品拆分为多个01背包物品来处理,或者直接使用二进制优化的方法,将物品数量分解为若干个2的幂次方的组合。 ##### 解法 假设物品的数量为 `k`,可以将其拆分为 `1, 2, 4, ..., 2^m` 等部分,然后对这些部分进行01背包处理。 ```cpp for (int i = 0; i < n; ++i) { int num = s[i]; for (int k = 1; num > 0; k <<= 1) { int take = min(k, num); for (int j = W; j >= take * w[i]; --j) { dp[j] = max(dp[j], dp[j - take * w[i]] + take * v[i]); } num -= take; } } ``` #### 四:组合背包问题 组合背包问题要求从多个物品中选择一个组合,使得总容量不超过背包容量,并且总价值最大。这种可以通过动态规划来解决,其中 `dp[j]` 表示容量为 `j` 时的最大价值。 ##### 解法 对于每个物品,遍历容量从 `W` 到 `w[i]`,并更新 `dp[j]`。 ```cpp for (int i = 0; i < n; ++i) { for (int j = W; j >= w[i]; --j) { dp[j] = max(dp[j], dp[j - w[i]] + v[i]); } } ``` #### 五:分组背包问题 分组背包问题中,物品被分为若干组,每组中的物品只能选择一个。这种可以通过三循环来实现:外遍历组数,内遍历容量,最内遍历组内的物品。 ##### 解法 定义 `dp[j]` 表示容量为 `j` 时的最大价值。对于每组物品,遍历容量从 `W` 到 `w[i]`,并更新 `dp[j]`。 ```cpp for (int g = 0; g < group_count; ++g) { for (int j = W; j >= 0; --j) { for (int i = 0; i < group[g].size(); ++i) { if (j >= group[g][i].weight) { dp[j] = max(dp[j], dp[j - group[g][i].weight] + group[g][i].value); } } } } ``` #### 六:有依赖的背包问题 在有依赖的背包问题中,某些物品的选择依赖于其他物品是否被选中。例如,物品A的选择必须伴随着物品B的选择。这种可以通过构建物品之间的依赖关系图,并使用动态规划来解决。 ##### 解法 定义 `dp[j]` 表示容量为 `j` 时的最大价值。对于每个物品,考虑其依赖关系,并更新 `dp[j]`。 ```cpp for (int i = 0; i < n; ++i) { if (item[i].has_dependency) { for (int j = W; j >= item[i].weight + item[i].dependency_weight; --j) { dp[j] = max(dp[j], dp[j - item[i].weight - item[i].dependency_weight] + item[i].value + item[i].dependency_value); } } else { for (int j = W; j >= item[i].weight; --j) { dp[j] = max(dp[j], dp[j - item[i].weight] + item[i].value); } } } ``` #### 七:混合背包问题 混合背包问题结合了01背包、完全背包和多重背包的特点。这种可以通过分别处理每种类型的物品来解决。 ##### 解法 对于每种类型的物品,分别使用对应的处理方法。例如,01背包物品使用逆序循环,完全背包物品使用顺序循环,多重背包物品使用二进制优化。 ```cpp for (int i = 0; i < n; ++i) { if (item[i].type == 0) { // 01背包 for (int j = W; j >= item[i].weight; --j) { dp[j] = max(dp[j], dp[j - item[i].weight] + item[i].value); } } else if (item[i].type == 1) { // 完全背包 for (int j = item[i].weight; j <= W; ++j) { dp[j] = max(dp[j], dp[j - item[i].weight] + item[i].value); } } else if (item[i].type == 2) { // 多重背包 int num = item[i].count; for (int k = 1; num > 0; k <<= 1) { int take = min(k, num); for (int j = W; j >= take * item[i].weight; --j) { dp[j] = max(dp[j], dp[j - take * item[i].weight] + take * item[i].value); } num -= take; } } } ``` #### 八:二维费用背包问题 二维费用背包问题中,每个物品有两个不同的费用(如重量和体积),并且有两个不同的容量限制。这种可以通过扩展状态定义来解决。 ##### 解法 定义 `dp[i][j]` 表示在费用1为 `i` 和费用2为 `j` 时的最大价值。对于每个物品,遍历费用1和费用2的容量,并更新 `dp[i][j]`。 ```cpp for (int i = 0; i < n; ++i) { for (int j = W1; j >= w1[i]; --j) { for (int k = W2; k >= w2[i]; --k) { dp[j][k] = max(dp[j][k], dp[j - w1[i]][k - w2[i]] + v[i]); } } } ``` ### 相关问题 1. 如何处理01背包问题中的多重背包情况? 2. 二维费用背包问题的动态规划解法是什么? 3. 分组背包问题的动态规划解法是什么? 4. 如何将完全背包问题与01背包问题区分开来? 5. 有依赖的背包问题如何通过动态规划求解?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值