【讲题用】我们不可能搭好积木!绝对不行。(似乎可行?!)
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;
}

浙公网安备 33010602011771号