潜水员 ← 二维费用的背包问题

【题目来源】
https://www.acwing.com/problem/content/1022/

【题目描述】
潜水员为了潜水要使用特殊的装备。
他有一个带2种气体的气缸:一个为氧气,一个为氮气。
让潜水员下潜的深度需要各种数量的氧和氮。
潜水员有一定数量的气缸。
每个气缸都有重量和气体容量。
潜水员为了完成他的工作需要特定数量的氧和氮。
他完成工作所需气缸的总重的最低限度的是多少?

例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
如果潜水员需要5升的氧和60升的氮则总重最小为249(1,2或者4,5号气缸)。
你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。

【输入格式】
第一行有2个整数 m,n。它们表示氧,氮各自需要的量。
第二行为整数 k 表示气缸的个数。
此后的 k 行,每行包括ai,bi,ci,3个整数。这些各自是:第 i 个气缸里的氧和氮的容量及气缸重量。

【输出格式】
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的
最低值

【数据范围】
1≤m≤21,
1≤n≤79,
1≤k≤1000,
1≤ai≤21,
1≤bi≤79,
1≤ci≤800

【二维费用的背包问题算法分析】
二维费用的背包问题,即具有
两种限制条件的背包问题,它是常见背包问题的一个简单的常见扩展。也就是说,常见的背包问题都会存在二维费用的扩展。如二维费用的0-1背包问题、二维费用的完全背包问题、二维费用的多重背包问题、二维费用的分组背包问题等。

二维费用的背包问题,要求对于装入背包的每个物品 i,必须同时满足两种不同的限制条件 vol1[i] 与 vol2[i],且每种限制条件的上限分别为 V1 与 V2。若设将物品 i 装入背包可获得的价值为 val[i],请问怎么选择物品,可得到最大价值。

下面以二维费用的0-1背包问题为例,给出一般的二维费用背包问题的解题思路如下:
令 c[i][j][k] 表示将前 i 个物品装入限制条件1为 j、限制条件2为 k 时,可获得的最大价值。
根据求解普通0-1背包问题的状态转移方程的思路,相应可得二维费用的0-1背包问题的状态转移方程为:
c[i][j][k] = max(c[i−1][j][k], c[i−1][j−vol1[i]][k−vol2[i]] + val[i] )

类似于将普通0-1背包问题由二维优化为一维的思路(https://blog.csdn.net/hnjzsyjyj/article/details/126071689),可以将二维费用的0-1背包问题由三维优化为二维,从而达到降低算法时间复杂度的目的。优化为二维后的二维费用的0-1背包问题的状态转移方程为:c[j][k] = max(c[j][k], c[j−vol1[i]][k−vol2[i]] + val[i])

编写代码时,一般采用如下的3重循环:

for (i=1; i<=n; i++)   // 此行语句也常用 while(n--) 代替,其中的n为物品个数
	for (j=V1; j>=vol1[i]; j--)
		for (k=V2; k>=vol2[i]; k--) {
			c[j][k]=max(c[j][k],c[j-vol1[i]][k-vol2[i]]+val[i]);
		}

所求最大价值为c[V1][V2]。

【本题算法分析】
参考:https://blog.csdn.net/Jacob0824/article/details/123434167

(1)初始化的细节处理:(对于要求体积 “至少是” 问题的初始化处理)
● dp(0)(0)(0)=0:第一维为0(一件物品都不选),第二维和第三维体积均为0(氧气和氮气体积至少为0),这种情况是合法的,且min值为0。
● dp(0)(1,2,3...)(1,2,3...):第一维为0(一件物品都不选),第二维和第三维体积至少为i(1,2,3…),
根据实际,这些状态都是不合法的(无法求min),因此将它们初始化为 ∞ 即可(至于是+∞还是-∞取决于这个问题是求的最大值还是最小值)

(2)即使所需要的氧气或者氮气所需的是数量是负数,这种状态也是合法的,其与所需数量为0是等价的,可以通过所需数量为0来转移。


【算法代码】

#include<bits/stdc++.h>
using namespace std;

const int V1=22;
const int V2=80;
int c[V1][V2];

int main() {
	int ox,ni,T;
	cin>>ox>>ni>>T;

	memset(c,0x3f3f3f3f,sizeof(c));
	c[0][0]=0;

	while(T--) {
		int vol1,vol2,val;
		cin>>vol1>>vol2>>val;
		for(int j=ox; j>=0; j--) {
			for(int k=ni; k>=0; k--) {
				c[j][k]=min(c[j][k],c[max(0,j-vol1)][max(0,k-vol2)]+val);
			}
		}
	}

	cout<<c[ox][ni]<<endl;

	return 0;
}


/*
in:
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119

out:
249
*/




【参考文献】
https://www.acwing.com/file_system/file/content/whole/index/content/337508/
https://www.acwing.com/solution/content/97152/
https://blog.csdn.net/m0_63666897/article/details/124516938
https://www.cnblogs.com/cs-whut/p/16104881.html
https://www.acwing.com/problem/content/description/8/
https://www.acwing.com/solution/content/97088/
https://blog.csdn.net/weixin_51216553/article/details/123781424

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值