T1 [JZOJ6309] 完全背包

题目描述

2019-08-18 纪中NOIP模拟A组

数据范围

  2019-08-18 纪中NOIP模拟A组

分析

  首先显然可以把体积大价值小,或者相同体积中价值较小的物品舍弃

  然后有这样一个定理,在 $n$ 个整数中一定存在若干个整数之和为 $n$ 的倍数

  证明就是在所有前缀和(包括第 $0$ 项)中,必定存在两个前缀和模 $n$ 的余数相等,所以这两个数之间的区间和(前开后闭)是 $n$ 的倍数

  设 $s$ 为性价比最高的物品中 $a_i$ 最小的物品,$x$ 为最优情况下非 $s$ 物品的种类

  当 $x \geq a_s$ 时,可以将若干个 $a_i$ 之和为 $a_s$ 倍数的物品用 $s$ 替换,此时结果一定不会更劣

  所以一定存在 $x < a_s$ 的最优方案,这 $x$ 个物品的 $a_i$ 之和一定不会超过 $100a_s$

  这样我们就可以先取 $\lfloor \frac{m}{a_s} \rfloor - 100$ 个 $s$ 物品,再在剩下的空间里做多重背包

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <set>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 1000005

int n, tot, last;
ll m, f[N];

struct Data {
    int a, b;
} w[N], v[N];

bool cmp(Data x, Data y) {
    if (x.a != y.a) return x.a < y.a;
    return x.b > y.b;
}

int main() {
    scanf("%d%lld", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &w[i].a, &w[i].b);
    sort(w + 1, w + n + 1, cmp);
    for (int i = 1; i <= n; i++)
        if (w[i].b > last)
            last = w[i].b, v[++tot] = w[i];
    Data best = v[1];
    for (int i = 2; i <= tot; i++)
        if (best.a * v[i].b > v[i].a * best.b) best = v[i];
    ll t = m / best.a - 100;
    ll ans = t * best.b;
    m -= t * best.a;
    for (int i = 1; i <= tot; i++)
        for (int j = v[i].a; j <= m; j++)
            f[j] = max(f[j], f[j - v[i].a] + v[i].b);
    printf("%lld\n", ans + f[m]);

    return 0;
}
View Code

相关文章:

  • 2022-01-23
  • 2021-05-27
  • 2021-08-22
  • 2021-06-12
  • 2021-06-20
  • 2022-02-17
  • 2021-09-27
  • 2022-02-02
猜你喜欢
  • 2021-12-26
  • 2021-06-29
  • 2021-09-18
  • 2021-07-25
  • 2021-10-31
  • 2022-01-03
  • 2021-06-12
相关资源
相似解决方案