AcWing 1022. 宠物小精灵之收服

宠物小精灵之收服(二维费用背包问题)

原题链接:https://www.acwing.com/problem/content/1024/

思路

先做一个阅读理解

每一个小精灵只能收一次->01背包
接下来去考虑体积、价值是什么

经典的01背包只有一个限制:背包的体积

可以看出与经典的01背包不同,它有两个限制,一个是精灵球的数量,一个是皮卡丘的体力(这个题中可以剖析出来皮卡丘的体力不能为0,枚举体力时记得处理枚举边界)

dp思路:(仿照01背包)

状态表示:
	集合:f[i][j][u]表示的是从前i个精灵中选,权值1(体力v1[i])不能超过j权值2(球数v2[i])不能超过u的所有方案的集合
	属性:max(收集的精灵的数量的最大值)
状态转移:
		[1,2,3...,i,...,k]
	第i个精灵选与不选:
	不选:f[i][j][u] = f[i-1][j][u]
	选: if(j-v1[i] >= 0 && u-v2[i] >= 0) f[i][j][u] = max(f[i][j][u],f[i-1][j-v1[i]][u-v2[i]]+1) (体力和球的数量要足够)

代码


朴素版的内存超限了:

#include<iostream>
using namespace std;

const int N = 1010,M = 510;
int f[110][M][N];
int n,m,k; // 球数量,体力,精灵数量
int v1[110],v2[110]; // 体力v1,球数量v2

int main()
{
    cin >> n >> m >> k;
    for(int i = 1; i <= k;i ++) cin >> v2[i] >> v1[i];
    
    for(int i = 1;i <= k;i ++)
    {
        for(int j = 1;j < m; j ++)
        {
            for(int u = 1;u <= n; u ++)
            {
                f[i][j][u] = f[i-1][j][u];
                if(j-v1[i] >= 0 && u-v2[i] >= 0) f[i][j][u] = max(f[i][j][u],f[i-1][j-v1[i]][u-v2[i]]+1);
            }
        }
    }
    cout << f[k][m-1][n] << ' ';
    
    // 求收服这么多精灵时所消耗的最少体力
    int t = m;
    while(t > 0 && f[k][m-1][n] == f[k][t - 1][n]) t --;
    cout << m - t ; // 剩余体力就是m-t
    
    return 0;
}


优化版(仿照01背包优化):

#include<iostream>
using namespace std;

const int N = 1010,M = 510;
int f[M][N];
int n,m,k; // 球数量,体力,精灵数量
int v1[110],v2[110]; // 体力v1,球数量v2

int main()
{
    cin >> n >> m >> k;
    for(int i = 1; i <= k;i ++) cin >> v2[i] >> v1[i];
    
    for(int i = 1;i <= k;i ++)
    {
        for(int j = m - 1;j >= v1[i]; j --)
        {
            for(int u = n;u >= v2[i]; u --)
            {
                f[j][u] = max(f[j][u],f[j-v1[i]][u-v2[i]]+1);
            }
        }
    }
    cout << f[m-1][n] << ' ';
    
    // 求收服这么多精灵时所消耗的最少体力
    int t = m;
    while(t > 0 && f[m-1][n] == f[t - 1][n]) t --;
    cout << m - t ; // 剩余体力就是m-t
    
    return 0;
}

posted @ 2022-10-26 23:48  r涤生  阅读(72)  评论(0编辑  收藏  举报