1022. 宠物小精灵之收服

1022. 宠物小精灵之收服

题意:

现在有n个胶囊,m个生命值,k个怪物,每个怪物需要a[i]个胶囊,且会造成b[i]个伤害后才能捕获,问在活着的前提下,最多捕获多少怪物,在怪物最多的情况下剩余生命值最大是多少
数据范围:
0<N≤1000,
0<M≤500,
0<K≤100

题解:

仔细分析题目就可以得到:这个是01背包的延伸,01背包中是空间和价钱,这个是胶囊和伤害
设f[i][j]表示刚好花费i个胶囊,j个生命值所捕获的怪物最大数量
注意f一开始要初始无限大
可以得到转移方程:
(01背包的延伸)

f[0][0] = 0;
    for(int i = 1; i <= K; i++) {
    
    
        for(int j = n; j >= w[i]; j--) 
            for(int k = m; k >= v[i]; k--)
                f[j][k] = max(f[j][k], f[j - w[i]][k - v[i]] + 1);
    }

然后我们根据最大胶囊的情况选择花费最少的体力值,即为剩下最多的体力值
这样复杂度是O(nmk)
详细看代码:
但是本题可以优化:
我们先想想01背包:
体积w与价值v是可以互逆的
什么意思?
f[i]表示为体积为i能装的最大价值
我们也可以将f[i]表示为价值为i所需的最小体积
两者等价,但是我们只需要选择较小的那个就行
这样可以优化时间复杂度
在本题中,k的范围是额外小的,所以我们设
dp[i][j]表示正好花费体力i,收集j个怪物所用最小的精灵球的数量
这样复杂度是O(K2m)
结合数据范围:
O(nmk) = 5e7
O(K2m) = 5e6
本题是都能过,但是这种方法要掌握
(图中分别是第二种方法和第一种方法)
在这里插入图片描述

代码:

第一个代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1005, M = 505, S = 105;
int n, m, K, w[S], v[S], f[N][M];
int main() {
    
    
    memset(f, 0xcf, sizeof f);
    scanf("%d%d%d", &n, &m, &K);
    for(int i = 1; i <= K; i++)
        scanf("%d%d", w + i, v + i);

    f[0][0] = 0;
    for(int i = 1; i <= K; i++) {
    
    
        for(int j = n; j >= w[i]; j--) 
            for(int k = m; k >= v[i]; k--)
                f[j][k] = max(f[j][k], f[j - w[i]][k - v[i]] + 1);
    }
    //cout<<f[0][0]<<endl;
    int res = 0, t=0;
    for(int j = 1; j <= n; j++) {
    
    
        for(int k = 1; k < m; k++) {
    
    
            if(f[j][k] > res || (res == f[j][k] && k < t)) {
    
    
                res = f[j][k], t = k;
            }
        }
    }
    printf("%d %d\n", res ,m - t);
    return 0;
}

优化后的代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1005, M = 505, S = 105;
const int INF = 0x3f3f3f3f;
int n, m, K, f[M][S];
/*
f[i][j] 表示体力为 i, 收集了 j 个精灵 用的最小的精灵球数量
*/
int main() {
    
    
    memset(f, 0x3f, sizeof f);
    scanf("%d%d%d", &n, &m, &K);
    f[0][0] = 0;
    for (int i = 1, c, d; i <= K; i++) {
    
    
        scanf("%d%d", &c, &d);
        for (int j = m; j >= d; j--)
            for (int k = K; k >= 1; k--)
                if(f[j - d][k - 1] + c <= n)
                    f[j][k] = min(f[j][k], f[j - d][k - 1] + c);
    }
    for (int k = K; ~k; k--) {
    
    
        int  p = INF;
        for (int j = 0; j < m; j++) {
    
    
            if(f[j][k] != INF && j < p) 
                p = j;
        }
        if(p != INF)  {
    
     printf("%d %d\n", k, m - p); return 0; }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35975367/article/details/114379641