洛谷 P1833 樱花

Description

Luogu传送门

Solution

算法:多重背包

但是裸的多重背包复杂度 \(O(n*m*p)\)\(p\) 为物品个数),过不了此题,会 \(TLE\)

我们考虑优化。

多重背包有两种优化方法,一种是二进制拆分优化,另一种是单调队列优化

这里只介绍一种:二进制拆分优化多重背包

另一种以后有时间再补吧。

顾名思义,我们要把数进行二进制拆分,那么我们要拆分什么呢?

我们要把物品个数进行差分,例如有 19 个物品,我们要把它拆成 5个物品,每个物品个数分别为1, 2, 4,8,3。

不难证明,这 5 个的任意组合可以表示出 1 ~ 19 的任意一个数,这也是二进制拆分优化的原理。

我们把 19 拆成 5 个物品后,他们的价值和体积也要相应做出改变,即变为 个数 * 单个的价值/体积,跟总价 = 个数 * 单价一个原理。

然后我们直接对拆分后的数组跑一个01背包即可,复杂度 \(O(num * m)\)\(num\) 为拆分后的数组长度。

Code

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 10010;
int h1, m1, h2, m2, n, m, num;
int t[N], c[N], p[N], w[N * 15], v[N * 15], f[N];

void prework(){            //二进制拆分,可以看着代码理解一下
    for(int i = 1; i <= n; i++){
        int tmp = 1;
        while(p[i]){
            w[++num] = tmp * t[i];
            v[num] = tmp * c[i];
            p[i] -= tmp;
            tmp *= 2;
            if(p[i] < tmp){
                w[++num] = p[i] * t[i];
                v[num] = p[i] * c[i];
                break;
            }
        }
    }
}

int main(){
    scanf("%d:%d %d:%d %d", &h1, &m1, &h2, &m2, &n);
    m = (60 * h2 + m2) - (60 * h1 + m1);
    for(int i = 1; i <= n; i++){
        scanf("%d%d%d", &t[i], &c[i], &p[i]);
        if(!p[i]) p[i] = 1e4;            //赋成无穷大
    }
    prework();
    for(int i = 1; i <= num; i++)        //注意这里是num
        for(int j = m; j >= w[i]; j--)
            f[j] = max(f[j], f[j - w[i]] + v[i]);
    printf("%d\n", f[m]);
    return 0;
}

End

posted @ 2021-08-08 19:51  xixike  阅读(57)  评论(0编辑  收藏  举报