AcWing 1020. 潜水员

\(AcWing\) \(1020\). 潜水员

一、题目描述

潜水员为了潜水要使用特殊的装备。

他有一个带\(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\) 行,每行包括\(a_i,b_i,c_i\)\(3\)个整数。这些各自是:第 \(i\) 个气缸里的氧和氮的容量及气缸重量。

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

数据范围
\(1≤m≤21,1≤n≤79,1≤k≤1000,1≤a_i≤21,1≤b_i≤79,1≤c_i≤800\)

输入样例

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

输出样例

249

二、与普通二维背包费用问题的区别

  • 普通的二维背包费用问题
    \(f(i,j,k)\) 表示从前\(i\)个物品中选,且花费\(1\)不超过\(j\),花费\(2\)不超过\(k\)最大 价值

  • 潜水员
    \(f(i,j,k)\) 表示从前\(i\)个物品中选,且花费\(1\)不少于\(j\),花费\(2\)不少于\(k\)最小 价值

本题是一个 二维费用\(01\)背包问题 ,但和一般的 二维费用\(01\)背包问题 不同

这题要求的是 费用不少于 规定条件,因此我们需要对于 状态的定义 进行改变

闫氏\(DP\)分析法

这样分析完,似乎与普通的二维费用背包没有区别!这肯定是不可能的,我们必然是遗漏了些什么!!

考虑\(j-v_1<0\),\(k-v_2<0\)的情况!

有普通的二维费用背包问题中,\(j,k\)是不能进行超载的,超过了背包就太重, 背包就 了!

在本题中是 可以超载 的,理解一下超载是什么意思:

  • \(j\):氧气还需要\(j\)
  • \(k\):氮气还需要\(k\)

举栗子\(j=2,k=5\),就是氧气还需要\(2\)升,氮气还需要\(5\)升,现在出现的某个气瓶,氧气\(20\)升,氮气\(50\)升,一个就可以把你的需求满足,那么请问:你还需要氧气多少升、氮气多少升呢?

:不需要,都可以满足要求了,即\(j=0,k=0\),也就是\(f[i-1][0][0]+w\),而对于一个无欲无求的\(f[i-1][0][0]\)自然是等于\(0\),也就是\(f[i][j][k]=w\)

三、三维解法

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
const int M = 110;
int n, m, m1, m2;
int f[N][M][M];

int main() {
    cin >> m1 >> m2 >> n;
    memset(f, 0x3f, sizeof f);
    f[0][0][0] = 0;

    for (int i = 1; i <= n; i++) {
        int v1, v2, w;
        cin >> v1 >> v2 >> w;
        for (int j = 0; j <= m1; j++)
            for (int k = 0; k <= m2; k++) {
                f[i][j][k] = f[i - 1][j][k];
                f[i][j][k] = min(f[i - 1][j][k], f[i - 1][max(0, j - v1)][max(0, k - v2)] + w);
            }
    }
    cout << f[n][m1][m2] << endl;
    return 0;
}

四、二维解法

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

const int N = 22, M = 80;

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

int main() {
    cin >> n >> m >> K;

    memset(f, 0x3f, sizeof f);
    f[0][0] = 0;

    while (K--) {
        int v1, v2, w;
        cin >> v1 >> v2 >> w;
        for (int i = n; i >= 0; i--)
            for (int j = m; j >= 0; j--)
                f[i][j] = min(f[i][j], f[max(0, i - v1)][max(0, j - v2)] + w);
    }

    cout << f[n][m] << endl;

    return 0;
}
posted @ 2021-12-14 20:42  糖豆爸爸  阅读(274)  评论(0编辑  收藏  举报
Live2D