zl程序教程

您现在的位置是:首页 >  其它

当前栏目

AcWing 1020. 潜水员

AcWing
2023-09-27 14:28:12 时间

题目传送门

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

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

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

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

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

二、闫氏DP分析法

注:上面的图有一个小瑕疵,右侧应该是

\[f(i-1,j-v_1,k-v_2)+w \]

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

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

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

在本题中,是可以超载的,根据题意理解一下超载是什么意思:
就是\(v_1>j\),\(v_2>k\),用人话说就是 当前气缸提供的氧气或者当前气缸提供的氮气已经足够满足题目要求!如果是这样的情况,那么不需要考虑之前什么样,自己本身就达标了,不用从前面的递推过来!

此时,不是从\(f[i-1,j-v_1,k-v_2]+w\)转移过来,而是直接从\(f[0,0,0]+w\)来就行了。

三、三维朴素解法

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;
const int M = 110;
int f[N][M][M];
int n, m1, m2;

//二维费用01背包-不少于维度费用,求最小代价
int main() {
    //注意次序
    scanf("%d %d %d", &m1, &m2, &n);
    //求最小值
    memset(f, 0x3f, sizeof f);
    f[0][0][0] = 0;

    for (int i = 1; i <= n; i++) {
        int v1, v2, w;
        scanf("%d %d %d", &v1, &v2, &w);
        for (int j = 0; j <= m1; j++)
            for (int k = 0; k <= m2; k++) {
                //不选择i号物品
                f[i][j][k] = f[i - 1][j][k];
                //分情况讨论

                //物品i加上就够一维使用,此时,只关心二维情况即可
                if (j - v1 < 0 && k - v2 >= 0)
                    f[i][j][k] = min(f[i][j][k], f[i - 1][0][k - v2] + w);
                //物品i加上就够二维使用,此时,只关心一维情况即可
                else if (j - v1 >= 0 && k - v2 < 0)
                    f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][0] + w);
                //如果选择了i号物品,两个维度直接拉满,那么只选择一个i就足够用,它参选的价值是w
                else if (j - v1 < 0 && k - v2 < 0)
                    f[i][j][k] = min(f[i][j][k], w);
                else
                    //正常递推
                    f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][k - v2] + w);
            }
    }
    printf("%d\n", f[n][m1][m2]);
    return 0;
}

四、三维优化解法

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;
const int M = 85;
int f[N][M][M]; //选择前i个气缸,且花费1不小于j,花费2不小于k的最小重量
int n, m1, m2;

int main() {
    //注意录入次序
    scanf("%d %d %d", &m1, &m2, &n);
    //求最小值
    memset(f, 0x3f, sizeof f);
    f[0][0][0] = 0;

    for (int i = 1; i <= n; i++) {
        int v1, v2, w;
        scanf("%d %d %d", &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][j][k], f[i - 1][max(j - v1, 0)][max(k - v2, 0)] + w);
            }
    }
    printf("%d\n", f[n][m1][m2]);
    return 0;
}

五、三维+滚动数组解法

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;
const int M = 85;
int f[2][M][M]; //选择前i个气缸,且花费1不小于j,花费2不小于k的最小重量
int n, m1, m2;

int main() {
    //注意录入次序
    scanf("%d %d %d", &m1, &m2, &n);
    //求最小值
    memset(f, 0x3f, sizeof f);
    f[0 & 1][0][0] = 0;

    for (int i = 1; i <= n; i++) {
        int v1, v2, w;
        scanf("%d %d %d", &v1, &v2, &w);
        for (int j = 0; j <= m1; j++)
            for (int k = 0; k <= m2; k++) {
                f[i & 1][j][k] = f[i - 1 & 1][j][k];
                f[i & 1][j][k] = min(f[i & 1][j][k], f[i - 1 & 1][max(j - v1, 0)][max(k - v2, 0)] + w);
            }
    }
    printf("%d\n", f[n & 1][m1][m2]);
    return 0;
}

六、二维解法

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;
const int M = 100;

int f[M][M];
int m1, m2;
int n;
int main() {
    scanf("%d %d %d", &m1, &m2, &n);

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

    for (int i = 1; i <= n; i++) {
        int v1, v2, w;
        scanf("%d %d %d", &v1, &v2, &w);
        for (int j = m1; j >= 0; j--)
            for (int k = m2; k >= 0; k--)
                f[j][k] = min(f[j][k], f[max(j - v1, 0)][max(k - v2, 0)] + w);
    }
    printf("%d\n", f[m1][m2]);
    return 0;
}