【讲题用】我们不可能搭好积木!绝对不行。(似乎可行?!)

题

Description

有 \(n\) 块积木,从这 \(n\) 块积木中挑选若干块,以任意顺序垂直堆成一列。

每块积木 \(i\) 都有三个属性:

  • 重量 \(w_i\)
  • 最大承重 \(s_i\)
  • 美观度 \(v_i\)

约束:

  • 所有积木重量总和不超过 \(W\)
  • 第 \(i\) 块积木上方的所有积木重量总和不超过 \(s_i\)。

求基座上所有积木的美观度总和的最大值。

Solution

贪心(邻项交换法)、背包 DP

假设某一合法列中,相邻两个积木:\(w_i+s_i > w_{i+1}+s_{i+1}\)。

交换这两个积木:

  • \(i\) 之前和 \(i+1\) 之后无影响。
  • \(i\):\(s'_i=s_{i+1}\ge \sum_{k=1}^{i}w_k>\sum_{k=1}^{i-1}w_k=\sum_{k=1}^{i-1}w'_k\)。
  • \(i+1\):\(s'_{i+1}=s_i>w_{i+1}-w_i+s_{i+1}\ge w_{i+1}-w_i+\sum_{k=1}^{i}w_k=\sum_{k=1}^{i}w'_k\)。

交换后不劣。于是可先按 \(w_i+s_i\) 升序排序。

然后就是裸的01背包。

Code

#include <bits/stdc++.h>
typedef long long i64;
using namespace std;

const i64 NINF = -(1ll << 60);

struct Block { i64 w, s, v; };

bool cmp(Block a, Block b) {
    return a.s + a.w < b.s + b.w;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, W;
    cin >> n >> W;
    vector<Block> d(n + 1);
    vector<i64> f(W + 1, NINF);
    f[0] = 0;
    for (int i = 1; i <= n; ++i)
        cin >> d[i].w >> d[i].s >> d[i].v;
    sort(d.begin() + 1, d.end(), cmp);
    for (int i = 1; i <= n; ++i)
        for (int j = W; j >= d[i].w; --j)
            if (d[i].s >= j - d[i].w)
                f[j] = max(f[j], f[j - d[i].w] + d[i].v);
    i64 res = 0;
    for (int i = 0; i <= W; ++i)
        res = max(res, f[i]);
    cout << res << '\n';
    return 0;
}
posted @ 2026-07-28 22:01  SHUddol  阅读(8)  评论(0)    收藏  举报