UVA-1025 A Spy in the Metro
题目
某城市的地铁是线性的,有n(2≤n≤50)个车站,从左到右编号为1~n。有M1辆列车从第1站开始往右开,还有M2辆列车从第n站开始往左开。在时刻0,Mario从第1站出发,目的是在时刻T(0≤T≤200)会见车站n的一个间谍。在车站等车时容易被抓,所以她决定尽量躲在开动的火车上,让在车站等待的总时间尽量短。列车靠站停车时间忽略不计,且Mario身手敏捷,即使两辆方向不同的列车在同一时间靠站,Mario也能完成换乘。
输入第1行为n,第2行为T,第3行有n-1个整数表示地铁从车站i到i+1的行驶时间(两个方向一样)。第4行为M1(1≤M1≤50),即从第1站出发向右开的列车数目。第5行包含M1个整数d1, d2,…, dM1(0≤di≤250,di<di+1),即各列车的出发时间。第6、7行描述从第n站出发向左开的列车,格式同第4、5行。输出仅包含一行,即最少等待时间。无解输出impossible。
分析
按照时间流逝,定义状态(i, j),表示时刻 i,到达 j站台。
先预处理0到T时间里,每个列车所处的状态,如果 i 时刻刚好到达 j 站,那么我们说(i, j)有向左/右的列车。这是为后面dp准备。
dp阶段,定义dp[i][j] 表示再时刻 i,你在车站 j 最少还要等多少时间。
由题意知:dp[T][n] = 0; 因为T时刻要求到达n站台。
对于其他状态,有三种决策,要选择最优决策:
- 等一个单位时间
- 搭成向右开的车(如果存在)
- 搭成向左开的车(如果存在)
具体实现从T时刻枚举到0时刻。
复杂度:O(nT)
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define d(x) cout << (x) << endl
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 5e2 + 10;
int n, T;
int m1, m2, d, cas = 1;
int dp[N][N]; //dp[i][j] 表示再时刻 i,你在车站 j 最少还要等多少时间
int t[N]; //相邻车站行驶时间
int l[N][N], r[N][N]; //在 i时刻,车站j,是否有向左或右开的车
void DP()
{
for(int i = 1; i <= n-1; i++){
dp[T][i] = INF; //T时刻如果在其他站,就永远到不了n
}
dp[T][n] = 0; //T时刻只能在n站
for(int i = T-1; i >= 0; i--){ //从时间 T 往前推
for (int j = 1; j <= n; j++){
dp[i][j] = dp[i + 1][j] + 1;
if(j < n && r[i][j] && i+t[j] <= T)
dp[i][j] = min(dp[i][j], dp[i + t[j]][j + 1]); //右
if(j > 1 && l[i][j] && i+t[j-1] <= T)
dp[i][j] = min(dp[i][j], dp[i + t[j - 1]][j - 1]);//左
}
}
}
int main()
{
while(cin >> n >> T && n){
for (int i = 1; i <= n-1; i++){
cin >> t[i]; //每个车站向右边车站的行驶时间
}
memset(l, 0, sizeof(l));
memset(r, 0, sizeof(r));
cin >> m1;
while(m1--){ //向右开的列车
cin >> d; //对于每个列车,计算到达每个站的时间
for(int j = 1; j <= n-1; j++){
if(d <= T)
r[d][j] = 1;
d += t[j];
}
}
cin >> m2;
while(m2--){ //向左开的列车
cin >> d;
for (int j = n - 1; j >= 1; j--){
if(d <= T)
l[d][j + 1] = 1;
d += t[j];
}
}
DP();
cout << "Case Number " << cas++ << ": ";
if(dp[0][1] >= INF)
cout << "impossible\n";
else
cout << dp[0][1] << endl;
}
return 0;
}