[01背包] 潜水员(01背包+二维费用背包+思维)

0. 前言

相关:

1. 01背包+二维费用+思维

1020. 潜水员

在这里插入图片描述在这里插入图片描述
本题重在总结三种体积的不同状态表示方法,初始化及答案。

  • 体积最多是 j:大多数背包问题
    • 初始化: 假设为 f[0,i],表示一个物品都不选,且体积最大是 i 的最大价值。那么就全部初始化为 0,因为所有物品都不选,那么体积不论是多少,其最大价值都是 0。这就只能求价值的最大值
  • 体积恰好是 j :典型题:[完全背包] 买书(完全背包+裸题)
    • 初始化:就只初始化 f[0][0]=0,其余都初始化为 INF,这个是用来求价值的最小值。反之求最大值,其余都初始化为 -INF 即可
  • 体积至少是 j:典型题:[01背包] 潜水员(01背包+二维费用背包)
    • 初始化: f[0][0]=0,其余是 INF,只能求价值最小值

思路:

  • 有问题的状态表示:
    • f[i,j,k]:所有从前 i 个物品中选,且氧气含量恰好j,氮气含量恰好k 的所有选法的气缸重量总和的最小值
  • 正确的状态表示:
    • f[i,j,k]:所有从前 i 个物品中选,且氧气含量至少j,氮气含量至少k 的所有选法的气缸重量总和的最小值
  • 状态计算:
    • 不选第 i 个物品:f[i-1,j,k]
    • 选第 i 个物品:f[i-1,j-vi,k-mi]+wi
    • 初始化f[0,0,0] = 0f[0,j,k]=INF 初始一个都不选,那么氧气含量和氮气含量就是 0,且总重量也是 0。其它由于需要求最小值,所有全部初始化为 INF
    • 以上两个状态转移和初始化方式,适用于上面两个状态表示…那么肯定是有问题的
    • 在有问题的状态表示中,状态转移时,f[i-1,j-vi,k-mi]+wi 需要保证 j>=vi,k>=mi,因为不能为负数。体积恰好是负数时的状态是不成立的
    • 在正确的状态表示中,状态表示的是体积至少是 j,那针对于当前物品的体积大于当前枚举的 j 的时候,该物品仍是可以使用的。例如,当前状态 f[2][3] 代表氧气含量至少是 2, 氮气含量至少是 3 的最小气缸重量,同时满足这两个条件才能发生状态转移。若现在有一个物品,氧气含量为 3,氮气含量为 2,那么它至少需要 3 的氧气容量, 2 个氮气容量,它也是一种合法状态,只不过现在是多占了一个氧气体积没有使用罢了,但是它在该状态定义下确确实实是一种合法状态。所以,如果用该物品的话,状态转移方程为:f[0][1]+w,表示氧气不再需要,再填补至少一个单位的氮气进来就行了。这样就能完美的进行状态转移。故,当氧气或者氮气枚举其体积一方小于 0 的时候,将该状态转移到 0 上去就行了。指其已经不需要再添进来该种气体了
    • 状态转移方程: f[i,j,k]=min(f[i-1,j,k],f[i-1,max(0,j-vi),max(0,k-mi)]
    • 优化到二维: f[j][k] = min(f[j][k], f[max(0, j - a)][max(0, k - b)] + w);
  • 答案:
    • 氧气含量至少为 n 氮气含量至少为 m 的最小气缸重量 f[n][m]

枚举体积的三种方式,注意细节和初始化方式。

这边大佬题解可参考:

代码:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1005, M = 505, K = 105;

int n, m, t;
int f[N][M];

int main() {
    cin >> n >> m >> t;
    for (int i = 0; i < t; ++i) {
        int a, b;
        cin >> a >> b;
        for (int j = n; j >= a; --j) 
            for (int k = m - 1; k >= b; --k) 
                f[j][k] = max(f[j][k], f[j - a][k - b] + 1);
    }
    cout << f[n][m - 1] << ' ';
    int k = m - 1;
    while (k > 0 && f[n][k - 1] == f[n][m - 1]) k --;
    cout << m - k << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值