疯狂的背包问题(12) - 二维费用问题
在容量和承重两维约束下做01背包:dp[j][k]表示容量j承重k的最大价值,两维均倒序转移。
OJ: luogu
题目 ID: U661994
难度:普及-
标签:动态规划背包二维费用背包
日期: 2026-08-08 23:13
题意
思路
一句话本质:多了一维容量,DP 状态从 dp[j] 变成 dp[j][k],转移仍然是倒序枚举两维,每个物品只选一次。
先看暴力:
py
import sys
def solve() -> None:
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
N = next(it)
V = next(it)
M = next(it)
items = []
for _ in range(N):
v = next(it)
w = next(it)
p = next(it)
items.append((v, w, p))
ans = 0
def dfs(idx: int, vol: int, weight: int, val: int) -> None:
nonlocal ans
if vol > V or weight > M:
return
if idx == N:
ans = max(ans, val)
return
v, w, p = items[idx]
dfs(idx + 1, vol, weight, val)
dfs(idx + 1, vol + v, weight + w, val + p)
dfs(0, 0, 0, 0)
print(ans)
if __name__ == '__main__':
solve()暴力 DFS 每个物品选/不选,时间复杂度
比普通 01 背包多了一维约束,DP 状态怎么扩展?
普通 01 背包的状态定义是
转移方程怎么写?
选择物品
两维枚举顺序无所谓(先 dp[j-v][k-w] 必须是处理当前物品之前的旧状态。
为什么这题只涉及 01 类型?
题面明确每种物品只有一件,所以是"二维费用的 01 背包"。如果某维约束对应的物品数量不同(如完全背包),只需把对应维改成正序即可,道理和混合背包中的 01/完全切换一样。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn = 105;
// dp[j][k] 表示费用1容量为 j、费用2容量为 k 时的最大价值。
int dp[maxn][maxn];
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int N, V, M;
cin >> N >> V >> M;
for (int i = 0; i < N; i++) {
int v, w, p;
cin >> v >> w >> p; // 费用1、费用2、价值
// 二维费用 0/1 背包:两维都倒序,保证每件物品只选一次。
for (int j = V; j >= v; j--)
for (int k = M; k >= w; k--)
dp[j][k] = max(dp[j][k], dp[j - v][k - w] + p);
}
cout << dp[V][M] << '\n';
return 0;
}复杂度
时间
总结
二维费用背包就是把 01 背包的"一维约束 + 一维 DP"直接扩充为"两维约束 + 二维 DP",转移逻辑不变,倒序保证每物品只选一次。理解了一维 01,二维只是加一层循环。