环形游乐场中的最优游览路线(带时间窗口和收益)
字数 8278 2025-12-11 23:29:15

环形游乐场中的最优游览路线(带时间窗口和收益)


题目描述

游乐场是一个环形道路,共有 \(n\) 个景点,编号从 \(0\)\(n-1\)(景点 \(n-1\) 的下一个景点是 \(0\))。环形道路的长度为 \(L\),相邻景点 \(i\)\((i+1)\bmod n\) 之间的距离为 \(d_i\)(已知,且 \(\sum_{i=0}^{n-1} d_i = L\))。
每个景点 \(i\) 有一个参观收益 \(v_i\) 和一个参观所需时间 \(t_i\)。你从任意一个景点出发,以单位速度沿环形道路行走(可顺时针或逆时针),到达某个景点的瞬间即可选择是否参观,若参观则花费时间 \(t_i\) 并获得收益 \(v_i\)(参观期间不能移动)。
你总共有 \(T\) 单位的时间(包括行走和参观时间),要求在时间 \(T\) 结束时回到起点景点。问在时间限制内,能获得的最大总收益是多少?


输入说明

  1. 整数 \(n\)\(2 \leq n \leq 200\)
  2. 整数 \(T\)\(1 \leq T \leq 1000\)
  3. 数组 \(d[0..n-1]\)\(1 \leq d_i \leq 100\)
  4. 数组 \(v[0..n-1]\)\(1 \leq v_i \leq 1000\)
  5. 数组 \(t[0..n-1]\)\(1 \leq t_i \leq 100\)

输出说明

一个整数,表示最大总收益。


示例(简化)

\(n=4, T=10\)
\(d = [2,3,1,4]\)
\(v = [5,8,3,6]\)
\(t = [1,2,1,2]\)
一种可行方案:从景点0出发,顺时针走到景点1(距离2),参观景点1(时间2,收益8),走到景点2(距离3),参观景点2(时间1,收益3),返回景点0(距离1+4=5)。总时间 = 行走(2+3+5)=10,参观(2+1)=3,总时间13>10?注意:必须保证行走+参观总时间 ≤ T,且结束时回到起点。
可见该示例中时间紧张,需精心选择景点。最优解需计算得出。


解题步骤(循序渐进)

步骤1:问题拆解与难点

  1. 环形处理:起点任意,但必须返回起点,构成一个环。
  2. 方向选择:可顺时针或逆时针走,但一旦选定方向,不能“调头”(因为时间是连续的,调头等价于提前折返,可通过状态设计包含)。
  3. 时间窗口:每个景点只能在到达时刻选择是否参观,且参观花费时间。
  4. 目标:在总时间 ≤ T 的条件下,最大化参观景点的收益之和。

步骤2:化环为链

常见技巧:由于起点任意,可枚举起点,然后破环成链。
但环形+双向移动,使得破环后仍需考虑两个方向出发的情况。
更优方法:将环形复制成两倍长度的链,然后考虑从任意起点出发,在长度为 \(n\) 的窗口内完成一个“回路”。
但本题要求必须返回起点,等价于从起点出发,走若干景点后沿原路返回或绕一圈返回,实际上可视为:

  • 从起点 \(s\) 出发,顺时针走一段(可能参观若干景点),然后返回起点;
  • 或逆时针走一段,然后返回起点;
  • 或顺时针走一段后再逆时针走另一段(但最终要返回起点,这等价于从起点分两个方向走,最终汇合在起点)。

然而,更简单的理解是:最终回到起点意味着总行走距离一定是环长 \(L\) 的整数倍?不一定,因为可以中途参观,走的路程可小于一圈或多圈。实际上,如果只在一个方向上走并返回,则总行走距离是两倍的“单程距离”。
为了简化,我们考虑从起点出发,只在一个方向上移动(顺时针或逆时针),并允许中途参观,最后原路返回起点。这样,总行走距离是“到达的最远景点与起点的距离”的两倍。
类似地,可以逆时针走另一侧。
更一般的情况是:从起点出发,顺时针走一段参观一些景点,再逆时针走另一段参观一些景点,最后回到起点,这等价于在起点分两个方向走,总行走距离是“顺时针最远距离 + 逆时针最远距离”的两倍。
为了统一,我们可以将环形展开成链,并枚举起点,然后考虑从起点向左(逆时针)和向右(顺时针)两个方向延伸的行程,但最后要回到起点,所以两个方向走的路径是对称的(去和回)。

但这样设计状态会复杂。一个更简洁的通用DP方法:区间DP,状态表示在某个环形区间上完成游览并返回起点的最大收益**。


步骤3:区间DP状态设计

我们定义:
\(dp[l][r][time]\) 表示在环上从景点 \(l\) 出发,顺时针走到景点 \(r\)(闭区间 [l, r] 这段弧),并且最终回到景点 \(l\),总时间消耗为 time 的条件下,能获得的最大收益
这里“最终回到起点 l”意味着:我们从 l 出发,顺时针参观 [l, r] 中的一些景点(每个最多一次),最后沿原路返回到 l。
行走距离 = 从 l 到 r 的顺时针距离 \(\text{dist}(l, r)\)两倍(因为去和回)。
参观时间是参观的景点的时间之和。
总时间 = 行走距离 + 参观总时间。

同理,可以定义逆时针的类似状态,但通过对称性,我们只需处理顺时针方向,然后在合并两个半环时计算总收益。

但题目中,我们可以从任意起点出发,走任意方向,最终回到起点,相当于选择环上一段连续的弧(可能走多段,但最终要回到起点,所以游览的景点集合是环上一段或多段,但多段的情况可视为两段:起点左右各一段)。
事实上,最优解一定是在起点两侧各选一段(可能为空),顺时针走右侧一段(并返回),逆时针走左侧一段(并返回),总时间相加 ≤ T。
那么我们可以分别计算:

  • 对每个起点 s,计算从 s 出发,顺时针走一段区间 \([s, s+k]\) 并返回 s 的“时间-收益”表(类似背包)。
  • 对每个起点 s,计算从 s 出发,逆时针走一段区间 \([s-k, s]\) 并返回 s 的“时间-收益”表。
    然后将顺时针和逆时针的两个表组合,得到从 s 出发的总收益。

步骤4:子问题求解(单方向区间DP)

我们先解决顺时针方向的问题:

定义 \(f[l][r][time]\) 表示从 l 出发,顺时针走到 r(闭区间),参观此区间内一些景点(可不全参观),最后停在 r(不返回 l),总时间恰好为 time 的最大收益。
为什么最后停在 r?因为这样方便递推:从小区间 [l, r] 扩展,可以是由 [l, r-1] 走到 r 并决定是否参观 r。
但我们要的是“返回起点”的情况,所以可以用 \(f\) 来计算:从 l 出发走到 r 并返回 l 的时间 = 走到 r 的时间 + 从 r 返回 l 的行走时间(距离是 \(\text{dist}(l, r)\)),注意返回途中不能参观新景点。
因此,从 l 到 r 并返回 l 的总时间 = time + \(\text{dist}(l, r)\),收益 = \(f[l][r][time]\)
这里 time 是“去程+参观”的时间,不包括返程行走时间。


步骤5:\(f[l][r][time]\) 的递推

考虑区间 [l, r],最后访问的景点是 r(因为我们是顺时针走,最后到的景点一定是 r)。
那么,到达 r 之前,我们可能从 l 直接走到 r,也可能在中间某些景点停留参观。但为了 DP 方便,我们考虑最后一步是从某个中间点 m 走到 r。
但这样会涉及“在区间内跳着参观”的问题,DP 状态需要知道上一次参观的位置,状态会变多。

更常用的方法:将状态设计为 \(f[l][r][k]\),表示在区间 [l, r] 内,只参观区间内的某些景点,且最后参观的景点是 l 或 r**,总参观时间为 k 的最大收益。
这是区间 DP 常见思路:当我们从小区间扩展到大区间时,新增的点是 l-1 或 r+1,并且我们可以选择是否参观它。

但为了简化,我们采用另一种思路:将行走距离和参观时间一起考虑,直接算总时间。
\(dp[l][r]\) 表示从 l 出发顺时针走到 r 并返回 l 的最小时间(或给定时间内的最大收益),但这种形式不易同时处理收益最大化。

为了同时优化收益和时间,我们使用 DP 计算“在给定时间上限内,从 l 出发走到 r 并返回 l 的最大收益”。
具体地,用 \(F[l][r][t]\) 表示从 l 出发,在区间 [l, r] 内参观(可任意选择),最后返回 l,总行走+参观时间恰好为 t 的最大收益。
转移时,考虑最后一个参观的景点是 j(l ≤ j ≤ r):
我们从 l 走到 j(距离 \(\text{dist}(l, j)\)),参观 j(时间 \(t_j\)),然后从 j 走到 l(距离 \(\text{dist}(l, j)\)),但这之间在 j 之前可能还参观了其他景点。
可以把路径拆成:从 l 出发,在 [l, j-1] 内参观并返回 l,然后从 l 到 j,参观 j,返回 l。
但这样会重复计算 l 到 j 的路径两次(实际上从 l 到 j-1 并返回,再从 l 到 j 并返回,路径重叠,行走距离会多算)。
所以这样不行。


步骤6:正确区间 DP 定义

定义 \(dp[l][r][side][time]\)

  • \(side=0\) 表示当前在区间左端点 l
  • \(side=1\) 表示当前在区间右端点 r
  • time 是已用时间
  • 值表示最大收益

意义:我们考虑区间 [l, r] 内的景点,当前在左端点(或右端点),已经参观了这个区间内的某些景点(不一定是全部),并且当前位置是 l 或 r,已用时间 time,能得到的最大收益。
初始时,区间只有一个点 i,我们可以选择参观或不参观 i。

递推:从小区间 [l, r] 扩展到 [l-1, r] 或 [l, r+1]。
例如,从 [l, r] 且当前在 l,要扩展到 l-1,则有两种情况:

  1. 从 l 走到 l-1(行走时间 \(d_{l-1}\)),不参观 l-1,则状态变为 [l-1, r] 且当前位置在 l-1(因为 l-1 是新区间的左端点),时间增加 \(d_{l-1}\)
  2. 走到 l-1 并参观,则时间增加 \(d_{l-1} + t_{l-1}\),收益增加 \(v_{l-1}\)

类似地,从当前位置在 l,走到 r+1 也是类似,但要注意行走方向是顺时针还是逆时针,距离计算要用环形距离。

但这样状态是 \(O(n^2 \cdot T)\),n≤200, T≤1000 可以接受。


步骤7:环形处理

将环复制成两倍长度链:景点 0..n-1, 再接上 0..n-1。
然后枚举起点 s,在链上取区间 [s, s+n-1],在这个区间上做 DP,但我们只关心从 s 出发并回到 s 的情况,且总时间 ≤ T。

在这个区间上,我们设 \(dp[l][r][side][time]\) 表示在链区间 [l, r] 上,当前位置在端点(l 或 r),已用时间 time 的最大收益。
初始化:对于每个点 i 作为起点,不参观任何点时,dp[i][i][0][0] = 0(在左端点即右端点)。

转移:

  1. 从 dp[l][r][0] 扩展到 l-1:
    走到 l-1 不参观:新时间 = time + dist(l-1, l),新状态 dp[l-1][r][0][新时间] 收益不变。
    走到 l-1 参观:新时间 = time + dist(l-1, l) + t[l-1],新状态 dp[l-1][r][0][新时间] 收益 + v[l-1]。
  2. 从 dp[l][r][0] 扩展到 r+1:
    走到 r+1 不参观:新时间 = time + dist(l, r+1)? 注意,从 l 到 r+1 的行走距离是 dist(l, r+1),但 l 到 r 已经走过一些路径,实际上,当前位置在 l,要走到 r+1,必须经过 r,所以我们应走 l→r→r+1 吗?不对,因为区间 [l, r] 内的景点我们已经访问过一部分,但路径可能不是连续走的,我们只是记录了当前位置在 l,可能之前是从 r 走到 l 的,所以 l 到 r+1 必须沿环走,距离是 min(顺时针距离, 逆时针距离)。但这里我们只允许在区间端点向外扩展,所以从 l 到 r+1 应走 l→l-1→...→r+1 逆时针,或者 l→r→r+1 顺时针,取决于环形。

为了避免方向混乱,我们固定顺时针方向为正方向,距离 \(\text{dist}(i, j)\) 表示从 i 顺时针走到 j 的距离(j 可能在 i 后面或前面,我们用模运算处理)。
在链 [0, 2n-1] 上,我们只考虑区间长度 ≤ n 的情况,这样顺时针方向是 l 到 r 是递增的,不会绕圈。

那么,在链区间 [l, r] 上,从 l 到 r+1 的行走距离就是 d[r](相邻距离)。从 r 到 l-1 的行走距离是 d[l-1](注意方向,这里是从 r 逆时针走到 l-1,距离是 d[r] 吗?不,逆时针从 r 到 l-1 的距离是链上 r 到 l-1 的顺时针距离的补集,但因为我们是在链上区间扩展,l≤r,从 r 到 l-1 是向左走,距离是 d[r-1]? 实际上,从 r 到 r-1 的距离是 d[r-1],但这里 r 到 l-1 是多个距离之和。

为了简化,我们只允许从当前端点走到紧邻区间外的第一个点,即从 l 走到 l-1,或从 r 走到 r+1,这样行走距离就是相邻距离 d[l-1] 或 d[r]。

这样,DP 转移就统一了。


步骤8:转移方程

设相邻距离数组为 \(d[i]\) 表示 i 到 i+1 的距离。
状态 dp[l][r][0][time] 表示在区间 [l, r],当前在 l 点,已用时间 time 的最大收益。
状态 dp[l][r][1][time] 表示在区间 [l, r],当前在 r 点。

初始化:对每个 i,dp[i][i][0][0] = 0, dp[i][i][1][0] = 0。

转移:

  1. 从 dp[l][r][0] 扩展:
    a) 走到 l-1 不参观:
    time2 = time + d[l-1]
    if time2 ≤ T: dp[l-1][r][0][time2] = max(自身, dp[l][r][0][time])
    b) 走到 l-1 参观:
    time2 = time + d[l-1] + t[l-1]
    if time2 ≤ T: dp[l-1][r][0][time2] = max(自身, dp[l][r][0][time] + v[l-1])
  2. 从 dp[l][r][0] 走到 r+1:
    但当前在 l,要走到 r+1 必须先经过 r,所以不能直接走到 r+1。因此,从端点 0 只能走到 l-1,从端点 1 只能走到 r+1。

类似地,从 dp[l][r][1] 扩展:
a) 走到 r+1 不参观:
time2 = time + d[r]
if time2 ≤ T: dp[l][r+1][1][time2] = max(自身, dp[l][r][1][time])
b) 走到 r+1 参观:
time2 = time + d[r] + t[r+1]
if time2 ≤ T: dp[l][r+1][1][time2] = max(自身, dp[l][r][1][time] + v[r+1])

注意:参观景点时,收益加一次,时间加参观时间。


步骤9:计算答案

我们想要从起点 s 出发,最后回到起点 s。
在 DP 状态中,我们可能最后停在 l 或 r。
要回到 s,必须保证当前区间包含 s,且从当前位置走回 s 的时间加上已用时间 ≤ T。
但这样要额外算回程时间,比较麻烦。

更直接的方法:在 DP 中增加一维,表示是否回到过起点,但那样状态太多。
一个技巧:将起点 s 复制到链的中间位置,然后我们只取区间长度为 n 的窗口,计算从 s 出发并回到 s 的最大收益。
实际上,我们可以枚举起点 s,在链上取区间 [s, s+n-1],初始化 dp[s][s][0][0]=0,然后做区间扩展 DP,最后在所有状态中,如果当前位置是 p,要回到 s,需要额外时间 walk(p, s),总时间 ≤ T 则更新答案。

但这样要算很多次,复杂度 O(n^3·T) 可能太大。
优化:我们可以用 DP 计算从 s 出发,顺时针走一段并返回 s 的收益表,以及逆时针的收益表,然后合并。


步骤10:最终算法框架

  1. 复制环成链,长度 2n。
  2. 预处理顺时针距离前缀和,方便计算 dist(i, j)。
  3. 对每个起点 s (0 ≤ s < n):
    a) 计算顺时针收益表 g1[t]:从 s 出发,顺时针走一段区间并返回 s,在时间 t 内的最大收益。
    这可以通过区间 DP 在链区间 [s, s+n-1] 上计算,但只允许向右(顺时针)走,并必须返回 s。
    可以定义 f[i][time] 表示从 s 顺时针走到 i 并返回 s 的最大收益,用背包思想做。
    转移:f[i][time] = max(f[i-1][time - 2dist(s,i) - t[i]] + v[i], f[i-1][time - 2dist(s,i)]) 等。
    b) 同理计算逆时针收益表 g2[t]。
    c) 合并 g1 和 g2:枚举时间 t1 给顺时针,t2 给逆时针,t1+t2 ≤ T,收益 = g1[t1] + g2[t2],取最大值。
  4. 所有 s 的最大值即为答案。

由于篇幅限制,这里给出关键递推:
\(f[i][t]\) 表示从 s 顺时针走到 i(i ≥ s),并返回 s,总时间 ≤ t 的最大收益。
则:
\(f[i][t] = \max( f[i-1][t], \; f[i-1][t - 2*d(s,i) - t[i]] + v[i] )\)
其中 d(s,i) 是 s 到 i 的顺时针距离。
注意:这里假设在 i 参观,则去程距离 d(s,i),参观时间 t[i],返程距离 d(s,i),总行走参观时间 = 2*d(s,i) + t[i]。
另外,可能在 i 不参观,则 f[i][t] 至少为 f[i-1][t]。

但这样只考虑了最后一站是 i 的情况,实际上中间可以参观多个点,所以这实为分组背包:每个点 i 是一个物品,重量 = 2*d(s,i) + t[i],价值 = v[i],但必须按顺序访问,所以用 DP 更新。

具体实现时,用一维背包 DP,对 i 从 s 到 s+n-1,时间从 T 到 0 倒序更新。

同理逆时针方向。

合并时,注意起点 s 的参观收益只能算一次,所以初始化 g1[0]=0, g2[0]=0,但合并时如果两边都包含 s 的收益,要减去重复的。可以在初始化时,先单独处理起点 s 的参观。


步骤11:总结

这道题是环形区间 DP 与背包问题的结合。
核心是将环形问题转化为:从起点出发,顺时针一段、逆时针一段,最后回到起点。
分别用背包 DP 计算两个方向的“时间-收益”表,然后合并。
时间复杂度 O(n^2·T),在给定数据范围可行。

环形游乐场中的最优游览路线(带时间窗口和收益) 题目描述 游乐场是一个环形道路,共有 \( n \) 个景点,编号从 \( 0 \) 到 \( n-1 \)(景点 \( n-1 \) 的下一个景点是 \( 0 \))。环形道路的长度为 \( L \),相邻景点 \( i \) 和 \( (i+1)\bmod n \) 之间的距离为 \( d_ i \)(已知,且 \( \sum_ {i=0}^{n-1} d_ i = L \))。 每个景点 \( i \) 有一个参观收益 \( v_ i \) 和一个参观所需时间 \( t_ i \)。你从任意一个景点出发,以单位速度沿环形道路行走(可顺时针或逆时针), 到达某个景点的瞬间即可选择是否参观 ,若参观则花费时间 \( t_ i \) 并获得收益 \( v_ i \)(参观期间不能移动)。 你总共有 \( T \) 单位的时间(包括行走和参观时间),要求 在时间 \( T \) 结束时回到起点景点 。问在时间限制内,能获得的最大总收益是多少? 输入说明 整数 \( n \)(\( 2 \leq n \leq 200 \)) 整数 \( T \)(\( 1 \leq T \leq 1000 \)) 数组 \( d[ 0..n-1] \)(\( 1 \leq d_ i \leq 100 \)) 数组 \( v[ 0..n-1] \)(\( 1 \leq v_ i \leq 1000 \)) 数组 \( t[ 0..n-1] \)(\( 1 \leq t_ i \leq 100 \)) 输出说明 一个整数,表示最大总收益。 示例(简化) \( n=4, T=10 \) \( d = [ 2,3,1,4 ] \) \( v = [ 5,8,3,6 ] \) \( t = [ 1,2,1,2 ] \) 一种可行方案:从景点0出发,顺时针走到景点1(距离2),参观景点1(时间2,收益8),走到景点2(距离3),参观景点2(时间1,收益3),返回景点0(距离1+4=5)。总时间 = 行走(2+3+5)=10,参观(2+1)=3,总时间13>10? 注意 :必须保证行走+参观总时间 ≤ T,且结束时回到起点。 可见该示例中时间紧张,需精心选择景点。最优解需计算得出。 解题步骤(循序渐进) 步骤1:问题拆解与难点 环形处理 :起点任意,但必须返回起点,构成一个环。 方向选择 :可顺时针或逆时针走,但一旦选定方向,不能“调头”(因为时间是连续的,调头等价于提前折返,可通过状态设计包含)。 时间窗口 :每个景点只能在到达时刻选择是否参观,且参观花费时间。 目标 :在总时间 ≤ T 的条件下,最大化参观景点的收益之和。 步骤2:化环为链 常见技巧:由于起点任意,可 枚举起点 ,然后破环成链。 但环形+双向移动,使得破环后仍需考虑两个方向出发的情况。 更优方法: 将环形复制成两倍长度的链 ,然后考虑从任意起点出发,在长度为 \( n \) 的窗口内完成一个“回路”。 但本题要求 必须返回起点 ,等价于从起点出发,走若干景点后沿原路返回或绕一圈返回,实际上可视为: 从起点 \( s \) 出发,顺时针走一段(可能参观若干景点),然后返回起点; 或逆时针走一段,然后返回起点; 或顺时针走一段后再逆时针走另一段(但最终要返回起点,这等价于从起点分两个方向走,最终汇合在起点)。 然而,更简单的理解是: 最终回到起点 意味着总行走距离一定是环长 \( L \) 的整数倍?不一定,因为可以中途参观,走的路程可小于一圈或多圈。实际上,如果只在一个方向上走并返回,则总行走距离是两倍的“单程距离”。 为了简化,我们考虑 从起点出发,只在一个方向上移动(顺时针或逆时针),并允许中途参观,最后原路返回起点 。这样,总行走距离是“到达的最远景点与起点的距离”的两倍。 类似地,可以逆时针走另一侧。 更一般的情况是:从起点出发,顺时针走一段参观一些景点,再逆时针走另一段参观一些景点,最后回到起点,这等价于在起点分两个方向走,总行走距离是“顺时针最远距离 + 逆时针最远距离”的两倍。 为了统一,我们可以 将环形展开成链 ,并枚举起点,然后考虑从起点向左(逆时针)和向右(顺时针)两个方向延伸的行程,但最后要回到起点,所以两个方向走的路径是对称的(去和回)。 但这样设计状态会复杂。一个更简洁的通用DP方法: 区间DP ,状态表示在某个环形区间上完成游览并返回起点的最大收益** 。 步骤3:区间DP状态设计 我们定义: \( dp[ l][ r][ time] \) 表示 在环上从景点 \( l \) 出发,顺时针走到景点 \( r \)(闭区间 [ l, r] 这段弧),并且最终回到景点 \( l \),总时间消耗为 time 的条件下,能获得的最大收益 。 这里“最终回到起点 l”意味着:我们从 l 出发,顺时针参观 [ l, r ] 中的一些景点(每个最多一次),最后沿原路返回到 l。 行走距离 = 从 l 到 r 的顺时针距离 \( \text{dist}(l, r) \) 的 两倍 (因为去和回)。 参观时间是参观的景点的时间之和。 总时间 = 行走距离 + 参观总时间。 同理,可以定义逆时针的类似状态,但通过对称性,我们只需处理顺时针方向,然后在合并两个半环时计算总收益。 但题目中,我们可以从任意起点出发,走任意方向,最终回到起点,相当于选择环上一段 连续的弧 (可能走多段,但最终要回到起点,所以游览的景点集合是环上一段或多段,但多段的情况可视为两段:起点左右各一段)。 事实上,最优解一定是在起点两侧各选一段(可能为空),顺时针走右侧一段(并返回),逆时针走左侧一段(并返回),总时间相加 ≤ T。 那么我们可以分别计算: 对每个起点 s,计算从 s 出发,顺时针走一段区间 \([ s, s+k ]\) 并返回 s 的“时间-收益”表(类似背包)。 对每个起点 s,计算从 s 出发,逆时针走一段区间 \([ s-k, s ]\) 并返回 s 的“时间-收益”表。 然后将顺时针和逆时针的两个表组合,得到从 s 出发的总收益。 步骤4:子问题求解(单方向区间DP) 我们先解决 顺时针方向 的问题: 定义 \( f[ l][ r][ time] \) 表示从 l 出发,顺时针走到 r(闭区间),参观此区间内一些景点(可不全参观), 最后停在 r (不返回 l),总时间恰好为 time 的最大收益。 为什么最后停在 r?因为这样方便递推:从小区间 [ l, r] 扩展,可以是由 [ l, r-1 ] 走到 r 并决定是否参观 r。 但我们要的是“返回起点”的情况,所以可以用 \( f \) 来计算:从 l 出发走到 r 并返回 l 的时间 = 走到 r 的时间 + 从 r 返回 l 的行走时间(距离是 \( \text{dist}(l, r) \)),注意返回途中不能参观新景点。 因此,从 l 到 r 并返回 l 的总时间 = time + \( \text{dist}(l, r) \),收益 = \( f[ l][ r][ time ] \)。 这里 time 是“去程+参观”的时间,不包括返程行走时间。 步骤5:\( f[ l][ r][ time ] \) 的递推 考虑区间 [ l, r ],最后访问的景点是 r(因为我们是顺时针走,最后到的景点一定是 r)。 那么,到达 r 之前,我们可能从 l 直接走到 r,也可能在中间某些景点停留参观。但为了 DP 方便,我们考虑 最后一步 是从某个中间点 m 走到 r。 但这样会涉及“在区间内跳着参观”的问题,DP 状态需要知道上一次参观的位置,状态会变多。 更常用的方法: 将状态设计为 \( f[ l][ r][ k] \),表示在区间 [ l, r] 内, 只参观区间内的某些景点,且最后参观的景点是 l 或 r** ,总参观时间为 k 的最大收益。 这是区间 DP 常见思路:当我们从小区间扩展到大区间时,新增的点是 l-1 或 r+1,并且我们可以选择是否参观它。 但为了简化,我们采用另一种思路: 将行走距离和参观时间一起考虑 ,直接算总时间。 设 \( dp[ l][ r ] \) 表示从 l 出发顺时针走到 r 并返回 l 的最小时间(或给定时间内的最大收益),但这种形式不易同时处理收益最大化。 为了同时优化收益和时间,我们使用 DP 计算“在给定时间上限内,从 l 出发走到 r 并返回 l 的最大收益”。 具体地,用 \( F[ l][ r][ t] \) 表示从 l 出发,在区间 [ l, r] 内参观(可任意选择), 最后返回 l ,总行走+参观时间恰好为 t 的最大收益。 转移时,考虑 最后一个参观的景点 是 j(l ≤ j ≤ r): 我们从 l 走到 j(距离 \( \text{dist}(l, j) \)),参观 j(时间 \( t_ j \)),然后从 j 走到 l(距离 \( \text{dist}(l, j) \)),但这之间在 j 之前可能还参观了其他景点。 可以把路径拆成:从 l 出发,在 [ l, j-1 ] 内参观并返回 l,然后从 l 到 j,参观 j,返回 l。 但这样会重复计算 l 到 j 的路径两次(实际上从 l 到 j-1 并返回,再从 l 到 j 并返回,路径重叠,行走距离会多算)。 所以这样不行。 步骤6:正确区间 DP 定义 定义 \( dp[ l][ r][ side][ time ] \): \( side=0 \) 表示当前在区间左端点 l \( side=1 \) 表示当前在区间右端点 r time 是已用时间 值表示最大收益 意义:我们考虑区间 [ l, r] 内的景点,当前在左端点(或右端点), 已经参观了这个区间内的某些景点 (不一定是全部),并且当前位置是 l 或 r,已用时间 time,能得到的最大收益。 初始时,区间只有一个点 i,我们可以选择参观或不参观 i。 递推:从小区间 [ l, r] 扩展到 [ l-1, r] 或 [ l, r+1 ]。 例如,从 [ l, r ] 且当前在 l,要扩展到 l-1,则有两种情况: 从 l 走到 l-1(行走时间 \( d_ {l-1} \)),不参观 l-1,则状态变为 [ l-1, r] 且当前位置在 l-1(因为 l-1 是新区间的左端点),时间增加 \( d_ {l-1} \)。 走到 l-1 并参观,则时间增加 \( d_ {l-1} + t_ {l-1} \),收益增加 \( v_ {l-1} \)。 类似地,从当前位置在 l,走到 r+1 也是类似,但要注意行走方向是顺时针还是逆时针,距离计算要用环形距离。 但这样状态是 \( O(n^2 \cdot T) \),n≤200, T≤1000 可以接受。 步骤7:环形处理 将环复制成两倍长度链:景点 0..n-1, 再接上 0..n-1。 然后枚举起点 s,在链上取区间 [ s, s+n-1 ],在这个区间上做 DP,但我们只关心从 s 出发并回到 s 的情况,且总时间 ≤ T。 在这个区间上,我们设 \( dp[ l][ r][ side][ time] \) 表示在链区间 [ l, r ] 上,当前位置在端点(l 或 r),已用时间 time 的最大收益。 初始化:对于每个点 i 作为起点,不参观任何点时,dp[ i][ i][ 0][ 0 ] = 0(在左端点即右端点)。 转移: 从 dp[ l][ r][ 0 ] 扩展到 l-1: 走到 l-1 不参观:新时间 = time + dist(l-1, l),新状态 dp[ l-1][ r][ 0][ 新时间 ] 收益不变。 走到 l-1 参观:新时间 = time + dist(l-1, l) + t[ l-1],新状态 dp[ l-1][ r][ 0][ 新时间] 收益 + v[ l-1 ]。 从 dp[ l][ r][ 0 ] 扩展到 r+1: 走到 r+1 不参观:新时间 = time + dist(l, r+1)? 注意,从 l 到 r+1 的行走距离是 dist(l, r+1),但 l 到 r 已经走过一些路径,实际上,当前位置在 l,要走到 r+1,必须经过 r,所以我们应走 l→r→r+1 吗?不对,因为区间 [ l, r ] 内的景点我们已经访问过一部分,但路径可能不是连续走的,我们只是记录了当前位置在 l,可能之前是从 r 走到 l 的,所以 l 到 r+1 必须沿环走,距离是 min(顺时针距离, 逆时针距离)。但这里我们只允许在区间端点向外扩展,所以从 l 到 r+1 应走 l→l-1→...→r+1 逆时针,或者 l→r→r+1 顺时针,取决于环形。 为了避免方向混乱,我们 固定顺时针方向为正方向 ,距离 \( \text{dist}(i, j) \) 表示从 i 顺时针走到 j 的距离(j 可能在 i 后面或前面,我们用模运算处理)。 在链 [ 0, 2n-1 ] 上,我们只考虑区间长度 ≤ n 的情况,这样顺时针方向是 l 到 r 是递增的,不会绕圈。 那么,在链区间 [ l, r] 上,从 l 到 r+1 的行走距离就是 d[ r](相邻距离)。从 r 到 l-1 的行走距离是 d[ l-1](注意方向,这里是从 r 逆时针走到 l-1,距离是 d[ r] 吗?不,逆时针从 r 到 l-1 的距离是链上 r 到 l-1 的顺时针距离的补集,但因为我们是在链上区间扩展,l≤r,从 r 到 l-1 是向左走,距离是 d[ r-1]? 实际上,从 r 到 r-1 的距离是 d[ r-1 ],但这里 r 到 l-1 是多个距离之和。 为了简化,我们只允许 从当前端点走到紧邻区间外的第一个点 ,即从 l 走到 l-1,或从 r 走到 r+1,这样行走距离就是相邻距离 d[ l-1] 或 d[ r ]。 这样,DP 转移就统一了。 步骤8:转移方程 设相邻距离数组为 \( d[ i ] \) 表示 i 到 i+1 的距离。 状态 dp[ l][ r][ 0][ time] 表示在区间 [ l, r ],当前在 l 点,已用时间 time 的最大收益。 状态 dp[ l][ r][ 1][ time] 表示在区间 [ l, r ],当前在 r 点。 初始化:对每个 i,dp[ i][ i][ 0][ 0] = 0, dp[ i][ i][ 1][ 0 ] = 0。 转移: 从 dp[ l][ r][ 0 ] 扩展: a) 走到 l-1 不参观: time2 = time + d[ l-1 ] if time2 ≤ T: dp[ l-1][ r][ 0][ time2] = max(自身, dp[ l][ r][ 0][ time ]) b) 走到 l-1 参观: time2 = time + d[ l-1] + t[ l-1 ] if time2 ≤ T: dp[ l-1][ r][ 0][ time2] = max(自身, dp[ l][ r][ 0][ time] + v[ l-1 ]) 从 dp[ l][ r][ 0 ] 走到 r+1: 但当前在 l,要走到 r+1 必须先经过 r,所以不能直接走到 r+1。因此,从端点 0 只能走到 l-1,从端点 1 只能走到 r+1。 类似地,从 dp[ l][ r][ 1 ] 扩展: a) 走到 r+1 不参观: time2 = time + d[ r ] if time2 ≤ T: dp[ l][ r+1][ 1][ time2] = max(自身, dp[ l][ r][ 1][ time ]) b) 走到 r+1 参观: time2 = time + d[ r] + t[ r+1 ] if time2 ≤ T: dp[ l][ r+1][ 1][ time2] = max(自身, dp[ l][ r][ 1][ time] + v[ r+1 ]) 注意:参观景点时,收益加一次,时间加参观时间。 步骤9:计算答案 我们想要从起点 s 出发,最后回到起点 s。 在 DP 状态中,我们可能最后停在 l 或 r。 要回到 s,必须保证当前区间包含 s,且从当前位置走回 s 的时间加上已用时间 ≤ T。 但这样要额外算回程时间,比较麻烦。 更直接的方法: 在 DP 中增加一维,表示是否回到过起点 ,但那样状态太多。 一个技巧: 将起点 s 复制到链的中间位置 ,然后我们只取区间长度为 n 的窗口,计算从 s 出发并回到 s 的最大收益。 实际上,我们可以枚举起点 s,在链上取区间 [ s, s+n-1],初始化 dp[ s][ s][ 0][ 0 ]=0,然后做区间扩展 DP,最后在所有状态中,如果当前位置是 p,要回到 s,需要额外时间 walk(p, s),总时间 ≤ T 则更新答案。 但这样要算很多次,复杂度 O(n^3·T) 可能太大。 优化:我们可以用 DP 计算 从 s 出发,顺时针走一段并返回 s 的收益表,以及逆时针的收益表,然后合并。 步骤10:最终算法框架 复制环成链,长度 2n。 预处理顺时针距离前缀和,方便计算 dist(i, j)。 对每个起点 s (0 ≤ s < n): a) 计算顺时针收益表 g1[ t ]:从 s 出发,顺时针走一段区间并返回 s,在时间 t 内的最大收益。 这可以通过区间 DP 在链区间 [ s, s+n-1 ] 上计算,但只允许向右(顺时针)走,并必须返回 s。 可以定义 f[ i][ time ] 表示从 s 顺时针走到 i 并返回 s 的最大收益,用背包思想做。 转移:f[ i][ time] = max(f[ i-1][ time - 2 dist(s,i) - t[ i]] + v[ i], f[ i-1][ time - 2 dist(s,i) ]) 等。 b) 同理计算逆时针收益表 g2[ t ]。 c) 合并 g1 和 g2:枚举时间 t1 给顺时针,t2 给逆时针,t1+t2 ≤ T,收益 = g1[ t1] + g2[ t2 ],取最大值。 所有 s 的最大值即为答案。 由于篇幅限制,这里给出关键递推: 设 \( f[ i][ t ] \) 表示从 s 顺时针走到 i(i ≥ s),并返回 s,总时间 ≤ t 的最大收益。 则: \( f[ i][ t] = \max( f[ i-1][ t], \; f[ i-1][ t - 2 d(s,i) - t[ i]] + v[ i ] ) \) 其中 d(s,i) 是 s 到 i 的顺时针距离。 注意:这里假设在 i 参观,则去程距离 d(s,i),参观时间 t[ i],返程距离 d(s,i),总行走参观时间 = 2 d(s,i) + t[ i ]。 另外,可能在 i 不参观,则 f[ i][ t] 至少为 f[ i-1][ t ]。 但这样只考虑了最后一站是 i 的情况,实际上中间可以参观多个点,所以这实为 分组背包 :每个点 i 是一个物品,重量 = 2* d(s,i) + t[ i],价值 = v[ i ],但必须按顺序访问,所以用 DP 更新。 具体实现时,用一维背包 DP,对 i 从 s 到 s+n-1,时间从 T 到 0 倒序更新。 同理逆时针方向。 合并时,注意起点 s 的参观收益只能算一次,所以初始化 g1[ 0]=0, g2[ 0 ]=0,但合并时如果两边都包含 s 的收益,要减去重复的。可以在初始化时,先单独处理起点 s 的参观。 步骤11:总结 这道题是环形区间 DP 与背包问题的结合。 核心是将环形问题转化为:从起点出发,顺时针一段、逆时针一段,最后回到起点。 分别用背包 DP 计算两个方向的“时间-收益”表,然后合并。 时间复杂度 O(n^2·T),在给定数据范围可行。