以每个地点作为最后一家店做线性 DP,转移时取满足距离大于 k 的前驱中最优的利润,答案取所有收尾位置的最大值。
OJ: roj
题目 ID: 1296
难度:普及-
标签:动态规划递推python
创建: 2026-09-30 03:41
更新: 2026-10-05 08:10
题目描述
直线上有
text
2
3 11
1 2 15
10 2 30
输出:
40
30思路
定义
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 03:41
# update_at: 2026-09-30 03:41
import sys
NONE = 0 # 位置升序,i 前面可能一家都接不上;利润恒正,0 就是「没有前驱」的哨兵
def max_profit(spots: list[int], profits: list[int], k: int) -> int:
"""求「任选若干地点且相邻两店距离严格大于 k」的最大利润和。
dp[i] 表示以第 i 个地点收尾时的最大利润:要么前面一家都不开(前驱利润记 NONE),
要么接在某个满足 spots[i] - spots[j] > k 的地点 j 后面。
每个 i 只依赖前面这一段的 dp 最优值,所以单独扫一遍前缀即可。
"""
dp = profits[:] # 初值 = 只开 i 这一家
for i in range(len(spots)):
# spots 升序,能接的前驱必定是前缀;取该前缀上 dp 的最大值
dp[i] += max((dp[j] for j in range(i) if spots[i] - spots[j] > k), default=NONE)
return max(dp)
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
T = next(data) # 测试数据组数
out: list[str] = []
for _ in range(T):
n, k = next(data), next(data) # 地点数、距离限制
spots = [next(data) for _ in range(n)] # 升序的位置序列 m
profits = [next(data) for _ in range(n)]
out.append(str(max_profit(spots, profits, k)))
print('\n'.join(out))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 08:10
* update_at: 2026-10-05 08:10
*/
#include <cstdio>
typedef long long ll;
const int MAXN = 105;
ll n, k; // 地点数、最小间距限制
ll m[MAXN]; // m[i]: 第 i 个地点的位置(升序)
ll p[MAXN]; // p[i]: 在第 i 个地点开店的利润
ll dp[MAXN]; // dp[i]: 以第 i 个地点作为最后一家店时的最大利润
// 处理一组数据:读入并做线性 DP
void solve() {
scanf("%lld %lld", &n, &k);
for (ll i = 1; i <= n; i++) scanf("%lld", &m[i]);
for (ll i = 1; i <= n; i++) scanf("%lld", &p[i]);
ll ans = 0;
// 位置升序,能接在 i 前面的店 j 一定满足 m[i] - m[j] > k
// 利润都为正,没有合法前驱时 dp[i] 就是 p[i] 自己
for (ll i = 1; i <= n; i++) {
dp[i] = p[i];
for (ll j = 1; j < i; j++)
if (m[i] - m[j] > k && dp[j] + p[i] > dp[i])
dp[i] = dp[j] + p[i];
if (dp[i] > ans) ans = dp[i]; // 答案取所有收尾位置的最大值
}
printf("%lld\n", ans);
}
int main() {
ll T;
scanf("%lld", &T);
for (ll t = 1; t <= T; t++) solve();
return 0;
}