lrj紫皮书268页
时间是单向流逝的,是一个天然的“序”。影响到决策的只有当前时间和所处的车站,所以可以用d(i,j)表示时刻i,你在车站j(编号为1~n),最少还需要等待多长时间。边界条件是d(T,n)=0,其他d(T,i)(i不等于n)为正无穷。有如下3种决策。
决策1:等1分钟。
决策2:搭乘往右开的车(如果有)。
决策3:搭乘往左开的车(如果有)。
在程序中定义一个数组has_train。has_train[t][i][0]表示时刻t,在车站i是否有往右开的火车,has_train[t][i][1]类似,不过记录的是往左开的火车。
状态有O(nT)个,每个状态最多只有3个决策,因此总时间复杂度为O(nT)。
——摘自《算法竞赛入门经典(第2版)》
AC代码
错误就是MAX一开始开成了50,导致RE;
因为没考虑到T的范围,误将T的范围也当成了N的范围
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX = 220; // 这里一开始开成了50,太小了导致RE
const int INF = (1<<30);
int dp[MAX][MAX]; // dp[i][j] 表示i时刻j站点在T时刻到n所用的最少等待时间
int has_train[MAX][MAX][2];//has_train[i][j][0] = true 表示i时刻j站点有向右的车可以乘坐
int main()
{
int n, T, t[MAX], M1, M2;
int tot = 0;
for (;;) {
tot++;
// 初始化 dp 和 has_train数组
memset(dp, 0, sizeof(dp));
memset(has_train, 0, sizeof(has_train));
memset(t, 0, sizeof(t));
// 读入数据,完成has_train
cin >> n;
if (n == 0) break;
cin >> T;
// 只有 1~n-1 ,t[0] t[n] == 0
for (int i = 1; i < n; i++) {
cin >> t[i];
}
// right
cin >> M1;
for (int k = 0; k < M1; k++) {
int a;
cin >> a;
for (int i = 1; i <= n; i++) {
has_train[a][i][0] = 1;
a += t[i];
}
}
// left
cin >> M2;
for (int k = 0; k < M2; k++) {
int b;
cin >> b;
for (int i = n; i >= 1; i--) {
has_train[b][i][1] = 1;
b += t[i-1];
}
}
// 开始动态规划处理
// T时刻已到达n站,T[n][0] = 0; 其他车站不可到达 为INF
for (int i = 1; i < n; i++) dp[T][i] = INF;
dp[T][n] = 0;
// 递推
for (int i = T - 1; i >= 0; i--) {
// 这个for表示:在某个时间点i,更新各个站点的状态
for (int j = 1; j <= n; j++) {
dp[i][j] = dp[i+1][j] + 1; // 原地等待一分钟
if (j < n && has_train[i][j][0] && i + t[j] <= T)
dp[i][j] = min(dp[i][j], dp[i+t[j]][j+1]); // right
if (j > 1 && has_train[i][j][1] && i + t[j-1] <= T)
dp[i][j] = min(dp[i][j], dp[i+t[j-1]][j-1]); // left
// 这三种情况枚举顺序执行完,dp[i][j] 就是这个状态下等待时间的最小值了
}
}
// 递归
//slove(0, 1);
cout << "Case Number " << tot << ": ";
if (dp[0][1] < INF) cout << dp[0][1] << endl;
else cout << "impossible" << endl;
}
return 0;
}