01背包变式(跑步、精卫填海)

  • 题意

题目链接:https://www.luogu.com.cn/problem/P1806 跑步
题目链接:https://www.luogu.com.cn/problem/P1510 精卫填海

01背包,就是每个物品只能拿一次,0 和 1, 拿或者不拿。
先讲一下原始版本,有 n 个物品,每个物品有一个体积 v,和价值 val,背包容量为 w ,问怎么拿使得总价值最大,且体积不超。

再讲一下这个变式。

跑步:就是甲准备在操场跑 n 圈,然后他跑步是分阶段的,比如说第一次跑 1 圈,然后第二次跑 2 圈,最后总共跑 n 圈。限制条件就是这次跑的圈数必须比上一次的圈数要多,假定第 0 次跑了 0 圈。问如果甲要跑 n 圈,有多少种方案?

转化成01背包 : 一开始我是没看出来,后面一看题解就悟了。首先圈数是递增的,也就是说从 1 到 n,每个数字只能出现一次,这不就是 01背包里每个物品只有拿或者不拿两种选择吗。不同的是这里拿的总价值有一个约束,必须是 n。然后就是写状态转移方程,f[i][j] += f[i-j][k]; f[i][j] 意思就是跑了 i 圈,然后最后一次跑了 j 圈。跑 j 圈之前一次跑了 k 圈。这就是转移方程。但是复杂度有点高。
优化一下,就是改成一维数组,f[j] += f[j - i]; 这也是跑了 j 圈,然后最后一圈跑了 i 圈。

精卫填海 : 精卫要叼石头填满一个区域体积为 w,石头还有 n 个,精卫叼这些石头所消耗的体力为 c,每块石头的体积为 v,精卫的体力为 k。问精卫把该区域填满后所剩下的最大体力,如果不可能填满则输出 Impossible。

转化成01背包 : 这很明显的01背包,不用转化了。只是说跟平常的01背包不一样。这里有三个变量都需要遍历,第一个就是遍历每个石头,第二个就是精卫的体力,第三个就是填了的体积。首先遍历每个石头是必须的,这不用说。再就是选体力还是体积了。如果选体积,那么数组就要存体力,而我们要求所剩下的最大体力。但是我们遍历石头的时候,每次都是减体力,而且最后输出也不好输出,总之就是不行,因为我一开始就是这么想的。而如果我们选体力的话,那就很好求了,数组里面存体积,每次更新的时候看一下体积是否能够填满该区域,如果能,那就更新最大体力值。最后输出也简单,直接输出就行。

  • 代码

跑步

#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<math.h>
#include<stack>
#include<map>
#include<list>
#include<unordered_set>
#include<unordered_map>
#define endl '\n';
//#define int long long;
using namespace std;
typedef long long ll; 
const int N = 2e5+10;
int n, m, k;
ll f[505];

void sovle(){
    cin >> n;
    f[0] = 1;

    for (int i = 1; i <= n; i ++){
        for (int j = n; j >= i; j --){
            f[j] += f[j - i];
        }
    }
    
    cout << f[n] - 1 << endl;
}

int main()
{	
    int t = 1; 
    //scanf("%d", &t);
    
    while (t --){
        sovle();
    }

    return 0;
}

精卫填海:

#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<math.h>
#include<stack>
#include<map>
#include<list>
#include<unordered_set>
#include<unordered_map>
#define endl '\n';
//#define int long long;
using namespace std;
typedef long long ll; 
const int N = 2e4+10;
int w, n, c, ans = -1;
int f[N], v[N], t[N]; 

void sovle(){
    cin >> w >> n >> c;
    for (int i = 1; i <= n; i ++){
        cin >> v[i] >> t[i];
    }

    //f[w] = c;

    for (int i = 1; i <= n; i ++){
        for (int j = c; j >= t[i]; j --){
            f[j] = max(f[j], f[j - t[i]] + v[i]);
            if (f[j] >= w)ans = max(ans, c - j);
        }
    }

    if (ans < 0){
        cout << "Impossible" << endl;
    }
    else {
        cout << ans << endl;
    }
}

int main()
{	
    int t = 1; 
    //scanf("%d", &t);
    
    while (t --){
        sovle();
    }

    return 0;
}
posted @ 2023-09-19 17:47  shunn  阅读(70)  评论(0)    收藏  举报