信息学奥赛一本通 1271:【例9.15】潜水员

【题目链接】

ybt 1271:【例9.15】潜水员

【题目考点】

1. 动态规划:二维费用背包

【解题思路】

y[i]:第i瓶的氧容量 d[i]:第i瓶的氮容量 w[i]:第i瓶的重量

1. 状态定义
  • 集合:选择气缸的方案
  • 限制:在前几个气缸中选择,需要满足至少多少氮气和氧气的容量
  • 属性:总重量
  • 条件:最小
  • 统计量:总重量

状态定义dp[l][i][j]:前l个气缸中选气缸,拥有至少i升氧气容量与j升氮气容量的方案中,总重量最少的方案中气缸的总重量。
初始状态:由于要求的是最小值,整个数组先初始化为无穷大。
在前0个气缸中选气缸,只能满足至少有0升氧气与0升氮气,总重量为0。所以dp[0][0][0] = 0

2. 状态转移方程

与01背包的思路一样,只不过比01背包多了一个费用维度

  • 集合:前l个气缸中选气缸,拥有至少i升氧气容量与j升氮气容量的方案
    分割集合:依据是否选择第l气缸进行分割

  • 子集1:如果选择第l气缸,那么接下来需要在前l-1个气缸中选择气缸,这些气缸应该拥有至少i-y[l]升氧气与j-d[l]升氮气。
    如果i-y[l]为负数,“这些气缸应该拥有至少负几升的氧气容量”这样的表述没有意义,由于气缸的氧气容量最小为0,因此该表述等价于“这些气缸应该拥有至少0升的氧气容量”。
    整合i-y[l]为非负数或负数的情况,应该说:这些气缸应该拥有至少max(0, i-y[l])升的氧气容量。同理,这些气缸应该拥有至少max(0, j-d[l])升的氮气容量。

    其最小总重量为dp[l-1][max(0, i-y[l])][max(0, j-d[l])],再加上第l气缸的重量w[l],得到:dp[l][i][j] = dp[l-1][max(0, i-y[l])][max(0, j-d[l])]+w[l]

  • 子集2:如果不选第l气缸,那么接下来需要在前l-1个气缸中选择气缸,这些气缸应该拥有至少i升氧气与j升氮气,其最小总重量为dp[l][i][j] = dp[l-1][i][j]

  • 以上两种情况取最小值

【题解代码】

解法1:二维费用背包 基本解法
#include <bits/stdc++.h>
using namespace std;
#define N 1005
int m, n, k, dp[N][25][80];//dp[i][j][k]: 前i个气缸中选气缸凑j升氧气k升氮气的最少重量 
int y[N], d[N], w[N];//y[i]:第i瓶的氧容量 d[i]:第i瓶的氮容量 w[i]:第i瓶的重量
int main()
{
    memset(dp, 0x3f, sizeof(dp));
    cin >> m >> n >> k;
    dp[0][0][0] = 0;
    for(int i = 1; i <= k; ++i)
        cin >> y[i] >> d[i] >> w[i];
    for(int l = 1; l <= k; ++l)
        for(int i = 0; i <= m; ++i)
            for(int j = 0; j <= n; ++j)
                dp[l][i][j] = min(dp[l-1][max(0, i-y[l])][max(0, j-d[l])]+w[l], dp[l-1][i][j]);
    cout << dp[k][m][n];
    return 0; 
}
解法2:二维费用背包 滚动数组优化
#include <bits/stdc++.h>
using namespace std;
#define M 100
#define N 1005
int m, n, k, dp[M][M];//dp[i][j][k]:(i被优化掉) 前i个气缸中选气缸凑j升氧气k升氮气的最少重量 
int y[N], d[N], w[N];//y[i]:第i瓶的氧容量 d[i]:第i瓶的氮容量 w[i]:第i瓶的重量
int main()
{
    memset(dp, 0x3f, sizeof(dp));
    cin >> m >> n >> k;
    dp[0][0] = 0;
    for(int i = 1; i <= k; ++i)
        cin >> y[i] >> d[i] >> w[i];
    for(int l = 1; l <= k; ++l)
        for(int i = m; i >= 0; --i)
            for(int j = n; j >= 0; --j)
                dp[i][j] = min(dp[max(0, i-y[l])][max(0, j-d[l])]+w[l], dp[i][j]);
    cout << dp[m][n];
    return 0; 
}
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值