把每个英雄的皮肤选择看成分组背包,按总花费做 DP,记录最多能得到多少种展示方式。
OJ: luogu
题目 ID: P5365
难度:普及+/提高
标签:动态规划背包
日期: 2026-06-19 17:40
题意
有 N 个英雄,第 i 个英雄有 K_i 款皮肤,每款皮肤的价格都是 C_i。
如果一个英雄买了 x 款皮肤,那么这个英雄对应的展示方式就有 x 种;如果没买皮肤,就不贡献展示方式。
现在要让“总展示方式数”至少达到 M,求最少花费。
这张表把题意翻成了背包模型:
| 原题对象 | 背包含义 |
|---|---|
| 一个英雄 | 一组物品 |
买 x 款皮肤 |
这组里选一个方案 |
花费 x * C_i |
这一方案的代价 |
展示方式数乘上 x |
这一方案的收益 |
思路
一句话本质:分组背包 + 乘法收益。每个英雄是一组,选
先看最直接的暴力:
import sys
data = list(map(int, sys.stdin.buffer.read().split()))
N, M = data[0], data[1]
K = data[2:2 + N]
C = data[2 + N:2 + N + N]
limit = sum(K[i] * C[i] for i in range(N))
best = limit + 1
def dfs(i, cost, ways):
global best
if ways >= M:
best = min(best, cost)
return
if i == N:
return
dfs(i + 1, cost, ways)
for x in range(2, K[i] + 1):
nc = cost + x * C[i]
nw = ways * x
if nw > M:
nw = M
dfs(i + 1, nc, nw)
dfs(0, 0, 1)
print(best)brute.py 对每个英雄枚举买
暴力里的决策有什么浪费?
注意
展示方式数是乘法累积的,怎么放进 DP?
普通背包累加价值,这里累乘。但本质一样——每选一个英雄的方案,就把"状态值"(展示方式数)乘以
怎么处理乘法溢出?
展示方式数可能巨大(每个英雄最多
转移怎么做?
对第
- 花费增加
- 展示方式数
(截断到 )
用备份数组
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N;
ll M;
int K[105], C[105]; // K[i] 上限展示量, C[i] 皮肤单价
// 乘法防溢出,超过 limit 就截断为 limit。
ll clamp_mul(ll a, int b, ll limit) {
__int128 v = (__int128)a * b;
if (v > limit) return limit;
return (ll)v;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N >> M;
for (int i = 0; i < N; i++) cin >> K[i];
for (int i = 0; i < N; i++) cin >> C[i];
int limit = 0;
for (int i = 0; i < N; i++) {
limit += K[i] * C[i]; // 花费的上限
}
// dp[cost] 表示花费 cost 元能达到的最大展示数量。
vector<ll> dp(limit + 1, 0);
dp[0] = 1;
for (int i = 0; i < N; i++) {
vector<ll> ndp = dp;
for (int cost = 0; cost <= limit; cost++) {
if (dp[cost] == 0) continue;
// 在当前英雄上买 x 份皮肤(x ≥ 2)
for (int x = 2; x <= K[i]; x++) {
int nc = cost + x * C[i];
if (nc > limit) break;
ndp[nc] = max(ndp[nc], clamp_mul(dp[cost], x, M));
}
}
dp.swap(ndp);
}
// 找到最小的花费使展示数量 ≥ M。
for (int cost = 0; cost <= limit; cost++) {
if (dp[cost] >= M) {
cout << cost << '\n';
return 0;
}
}
return 0;
}复杂度
- 时间复杂度:
,其中 S是总花费上界 - 空间复杂度:
由于 C_i <= 199,K_i <= 10,总花费上界仍然很小,所以这个 DP 是可行的。
总结
这题的本质是“分组背包 + 乘法收益”。
把每个英雄的皮肤数当成分组方案,按总花费做 DP,记录能得到的最大展示方式数,就能直接求出最小花费。
一图流解析
这张图把本题的建模、关键转移、实现检查和训练方法压缩到一页,适合读完正文后复盘。
