UVa 1336 Fixing the Great Wall
题目描述
长城上有 nnn 个损坏的区段需要修复,每个区段的位置为 pip_ipi,立即修复的成本为 cic_ici,每延迟一个时间单位修复,成本会增加 Δi\Delta_iΔi。GWARR\texttt{GWARR}GWARR 机器人从初始位置 xxx 出发,以速度 vvv 在长城上移动。一旦机器人到达某个损坏区段的位置,可以瞬间完成修复。要求确定修复顺序,使得总成本最小。
输入格式:
- 多组测试数据,每组第一行是 n,v,xn, v, xn,v,x
- 接下来 nnn 行,每行 pi,ci,Δip_i, c_i, \Delta_ipi,ci,Δi
- 输入以 n=v=x=0n = v = x = 0n=v=x=0 结束
输出格式:
- 每组数据输出一个整数,表示最小成本(向下取整)
题目分析
问题性质
这是一个典型的区间动态规划问题,具有以下特点:
- 时间累积成本:每个区段的修复成本随时间线性增长
- 移动时间成本:机器人移动需要时间,影响所有未修复区段的成本
- 位置相关性:所有区段位于一条直线上,修复顺序影响移动路径
关键观察
- 最优子结构:在最优修复序列中,任意连续区间 [i,j][i,j][i,j] 的修复也是最优的
- 区间扩展:可以从单个点开始,向左右扩展修复区间
- 状态记录:需要记录当前修复区间和机器人的最后位置
动态规划设计
定义两个 DP\texttt{DP}DP 数组:
- dpLeft[i][j]dpLeft[i][j]dpLeft[i][j]:修复区间 [i,j][i,j][i,j] 且机器人最后停在左端点 iii 时的最小成本
- dpRight[i][j]dpRight[i][j]dpRight[i][j]:修复区间 [i,j][i,j][i,j] 且机器人最后停在右端点 jjj 时的最小成本
状态转移:
- 从 dpLeft[i+1][j]dpLeft[i+1][j]dpLeft[i+1][j] 扩展到 dpLeft[i][j]dpLeft[i][j]dpLeft[i][j]:从 i+1i+1i+1 移动到 iii
- 从 dpRight[i+1][j]dpRight[i+1][j]dpRight[i+1][j] 扩展到 dpLeft[i][j]dpLeft[i][j]dpLeft[i][j]:从 jjj 移动到 iii
- 右端点扩展同理
成本计算:
移动时间 = 距离 / 速度
延迟成本 = 移动时间 × 所有未修复区段的 Δ\DeltaΔ 之和
算法步骤
- 将损坏区段按位置排序
- 添加虚拟起点(机器人初始位置)
- 计算 Δ\DeltaΔ 的前缀和,用于快速计算未修复区段的 Δ\DeltaΔ 和
- 初始化 DP\texttt{DP}DP 数组
- 按区间长度从小到大进行 DP\texttt{DP}DP
- 输出最终结果
复杂度分析
- 时间复杂度:O(n2)O(n^2)O(n2),需要填充 n×nn \times nn×n 的 DP\texttt{DP}DP 表
- 空间复杂度:O(n2)O(n^2)O(n2),存储两个 DP\texttt{DP}DP 数组
代码实现
// Fixing the Great Wall
// UVa ID: 1336
// Verdict: Accepted
// Submission Date: 2025-10-24
// UVa Run Time: 0.100s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <bits/stdc++.h>
using namespace std;const int MAX_N = 1005;
const double INF = 1e18;struct Section {int position;int baseCost;int deltaCost;bool operator<(const Section& other) const {return position < other.position;}
} sections[MAX_N];int numSections, robotSpeed, startPosition;
double dpLeft[MAX_N][MAX_N], dpRight[MAX_N][MAX_N];
int prefixSumDelta[MAX_N];// 获取区间 [l, r] 之外的 delta 成本之和
int getRemainingDeltaSum(int left, int right) {return prefixSumDelta[numSections] - (prefixSumDelta[right] - prefixSumDelta[left - 1]);
}int main() {ios::sync_with_stdio(false);cin.tie(0);while (cin >> numSections >> robotSpeed >> startPosition) {if (numSections == 0 && robotSpeed == 0 && startPosition == 0) break;// 读取损坏区段数据for (int i = 1; i <= numSections; i++) {cin >> sections[i].position >> sections[i].baseCost >> sections[i].deltaCost;}// 添加虚拟起点(机器人初始位置)numSections++;sections[numSections].position = startPosition;sections[numSections].baseCost = 0;sections[numSections].deltaCost = 0;// 按位置排序sort(sections + 1, sections + numSections + 1);// 计算 delta 成本的前缀和prefixSumDelta[0] = 0;for (int i = 1; i <= numSections; i++) {prefixSumDelta[i] = prefixSumDelta[i - 1] + sections[i].deltaCost;}// 找到机器人初始位置对应的索引int startIndex = -1;for (int i = 1; i <= numSections; i++) {if (sections[i].position == startPosition && sections[i].baseCost == 0 && sections[i].deltaCost == 0) {startIndex = i;break;}}// 初始化 DP 数组for (int i = 1; i <= numSections; i++) {for (int j = i; j <= numSections; j++) {dpLeft[i][j] = dpRight[i][j] = INF;}}dpLeft[startIndex][startIndex] = dpRight[startIndex][startIndex] = 0;// 区间动态规划for (int length = 2; length <= numSections; length++) {for (int i = 1; i + length - 1 <= numSections; i++) {int j = i + length - 1;// 从左边扩展:最后停在左端点 idouble costFromLeft = dpLeft[i + 1][j] + (sections[i + 1].position - sections[i].position) * 1.0 / robotSpeed * getRemainingDeltaSum(i + 1, j);double costFromRight = dpRight[i + 1][j] + (sections[j].position - sections[i].position) * 1.0 / robotSpeed * getRemainingDeltaSum(i + 1, j);dpLeft[i][j] = min(costFromLeft, costFromRight) + sections[i].baseCost;// 从右边扩展:最后停在右端点 jcostFromRight = dpRight[i][j - 1] + (sections[j].position - sections[j - 1].position) * 1.0 / robotSpeed * getRemainingDeltaSum(i, j - 1);costFromLeft = dpLeft[i][j - 1] + (sections[j].position - sections[i].position) * 1.0 / robotSpeed * getRemainingDeltaSum(i, j - 1);dpRight[i][j] = min(costFromRight, costFromLeft) + sections[j].baseCost;}}// 输出结果(向下取整)long long result = (long long)floor(min(dpLeft[1][numSections], dpRight[1][numSections]));cout << result << "\n";// 恢复 numSections 的原始值numSections--;}return 0;
}
总结
本题通过区间动态规划巧妙地解决了带时间累积成本的路径规划问题。关键点在于:
- 将机器人初始位置作为虚拟起点
- 使用两个 DP\texttt{DP}DP 数组分别记录停在区间两端的状态
- 利用前缀和快速计算未修复区段的延迟成本
这种解法在 n≤1000n \leq 1000n≤1000 的规模下具有很好的可行性,能够高效求出最小修复成本。
