疯狂的背包问题(15) - 泛化物品背包问题
物品价值随分配容量变化:分段线性插值得val[c]=f(c),然后倒序DP对所有容量c尝试分配x容量得val[x]。
OJ: luogu
题目 ID: U662012
难度:普及+/提高-
标签:动态规划背包泛化物品
日期: 2026-08-08 23:13
题意
思路
一句话本质:每个物品对应一个分段线性函数
先看暴力:
import sys
def solve() -> None:
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
N = next(it)
V = next(it)
f = []
for _ in range(N):
K = next(it)
points = []
for __ in range(K):
x = next(it)
y = next(it)
points.append((x, y))
vals = [0] * (V + 1)
for x in range(V + 1):
if x <= points[0][0]:
vals[x] = points[0][1]
elif x >= points[-1][0]:
vals[x] = points[-1][1]
else:
for j in range(len(points) - 1):
x1, y1 = points[j]
x2, y2 = points[j + 1]
if x1 <= x <= x2:
t = (x - x1) / (x2 - x1)
vals[x] = int(y1 + t * (y2 - y1))
break
f.append(vals)
ans = 0
def dfs(idx: int, remaining: int, cur_val: int) -> None:
nonlocal ans
if idx == N:
ans = max(ans, cur_val)
return
for x in range(remaining + 1):
dfs(idx + 1, remaining - x, cur_val + f[idx][x])
dfs(0, V, 0)
print(ans)
if __name__ == '__main__':
solve()暴力对每个物品枚举分配 0 到
泛化物品和普通 01 背包有什么区别?
普通 01 背包中,一个物品是固定的
怎么把这个函数变成 DP 能用的数据?
第一步是"离散化"——把函数变成一张表
有了 val[c] 后怎么做 DP?
对于每个泛化物品
这本质上是 01 背包的一种推广吗?
是的。普通 01 背包是泛化物品的特例:函数曲线只有两个点——分配
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int N, V;
cin >> N >> V;
// dp[j] 表示容量为 j 时的最大价值。
vector<int> dp(V + 1, 0);
for (int i = 0; i < N; i++) {
int K;
cin >> K; // 函数的关键点数量
vector<pair<int, int>> points(K);
for (int j = 0; j < K; j++)
cin >> points[j].first >> points[j].second;
// val[x] 表示该泛化物品消耗容量 x 时获得的价值(分段线性)。
vector<int> val(V + 1, 0);
for (int x = 0; x <= V; x++) {
if (x <= points[0].first) {
val[x] = points[0].second;
} else if (x >= points.back().first) {
val[x] = points.back().second;
} else {
for (int j = 0; j < K - 1; j++) {
int x1 = points[j].first, y1 = points[j].second;
int x2 = points[j + 1].first, y2 = points[j + 1].second;
if (x1 <= x && x <= x2) {
double t = (double)(x - x1) / (x2 - x1);
val[x] = (int)(y1 + t * (y2 - y1));
break;
}
}
}
}
// 0/1 背包倒序枚举容量,尝试分配给当前物品不同容量 x。
for (int j = V; j >= 0; j--)
for (int x = 0; x <= j; x++)
dp[j] = max(dp[j], dp[j - x] + val[x]);
}
cout << dp[V] << '\n';
return 0;
}复杂度
预处理每个物品的
空间
总结
泛化物品把"物品 = 固定 (体积, 价值)“推广为"物品 = 函数 (容量 → 价值)”,DP 思路也相应地从"选/不选"推广为"分配多少容量"。核心是两步:插值预计算