题目
思路
本题的难点在于抽象,由于时间本身就是一个天然的“序”,所以可以由时间和所在车站组成状态。
1.状态定义:(T,i),在T时刻位于i车站。
2.指针函数:d(T,i),还需在车站等待多长时间。
3.状态转移方程:
取决于对于每个状态,有三种决策:
- 等1分钟。
- 乘坐往右开的车,去向右第一个车站。(有车的话)
- 乘坐往做开的车,去向左第一个车站。(有车的话)
所以状态转移方程为:
d(T,i)=min(d(T+1,i)+1,d(T+time[i],i+1),d(T+time[i−1],i−1))
d
(
T
,
i
)
=
m
i
n
(
d
(
T
+
1
,
i
)
+
1
,
d
(
T
+
t
i
m
e
[
i
]
,
i
+
1
)
,
d
(
T
+
t
i
m
e
[
i
−
1
]
,
i
−
1
)
)
代码
1.记忆化搜索:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define _for(i,a,b) for(int i =(a); i<(b); i++)
#define _rep(i,a,b) for(int i =(a); i<=(b); i++)
using namespace std;
const int INF = 1<<30;
const int maxn = 1000+100;
int time[maxn], has_train[maxn][maxn][2], d[maxn][maxn];
int TT, n, M1, M2;
int dp(int T, int i){
int &ans = d[T][i];
if (ans != -1) return ans;
int d1 = dp(T+1,i)+1;
int d2 = INF;
if (i < n && has_train[T][i][0] && T+time[i] <= TT)
d2 = dp(T+time[i],i+1);
int d3 = INF;
if (i > 1 && has_train[T][i][1] && T+time[i-1] <= TT)
d3 = dp(T+time[i-1],i-1);
ans = min(min(d1,d2), min(d1,d3));
return ans;
}
int main(){
int kase = 0;
while(scanf("%d",&n) == 1 && n){
scanf("%d",&TT);
_rep(i,1,n-1) scanf("%d",&time[i]);
int x;
memset(has_train, 0, sizeof(has_train));
scanf("%d",&M1);
_for(i,0,M1){
scanf("%d",&x);
_rep(i,1,n-1) {
has_train[x][i][0] = 1;
x+=time[i];
}
}
scanf("%d",&M2);
_for(i,0,M2){
scanf("%d",&x);
for(int i = n; i>1; i--){
has_train[x][i][1] = 1;
x+=time[i-1];
}
} // 输入完成
memset(d,-1,sizeof(d));
_rep(i,1,n-1) d[TT][i] = INF; //小技巧:初始化一些无法达到终点的状态
d[TT][n] = 0; //递归边界
int ans = dp(0,1);
printf("Case Number %d: ",++kase);
if (ans >= INF) printf("impossible\n");
else printf("%d\n",ans);
}
return 0;
}
2.递推
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define _for(i,a,b) for(int i =(a); i<(b); i++)
#define _rep(i,a,b) for(int i =(a); i<=(b); i++)
using namespace std;
const int INF = 1 << 30;
const int maxn = 1000 + 100;
int time[maxn], has_train[maxn][maxn][2], d[maxn][maxn];
int TT, n, M1, M2;
int main() {
int kase = 0;
while (scanf("%d", &n) == 1 && n) {
scanf("%d", &TT);
_rep(i, 1, n - 1) scanf("%d", &time[i]);
int x;
memset(has_train, 0, sizeof(has_train));
scanf("%d", &M1);
_for(i, 0, M1) {
scanf("%d", &x);
_rep(i, 1, n - 1) {
has_train[x][i][0] = 1;
x += time[i];
}
}
scanf("%d", &M2);
_for(i, 0, M2) {
scanf("%d", &x);
for (int i = n; i>1; i--) {
has_train[x][i][1] = 1;
x += time[i - 1];
}
} // 输入完成
_rep(i, 1, n - 1) d[TT][i] = INF; // 表示无法到达
d[TT][n] = 0; // 表示已经到达
int d1, d2, d3;
for (int T = TT - 1; T >= 0; T--)
_rep(i, 1, n) {
d1 = d[T + 1][i] + 1;
d2 = INF, d3 = INF;
if (i < n && has_train[T][i][0] && T + time[i] <= TT)
d2 = d[T + time[i]][i + 1];
if (i > 1 && has_train[T][i][1] && T + time[i - 1] <= TT)
d3 = d[T + time[i - 1]][i - 1];
d[T][i] = min(min(d1, d2), min(d1, d3));
}
printf("Case Number %d: ", ++kase);
if (d[0][1] >= INF) printf("impossible\n");
else printf("%d\n", d[0][1]);
}
return 0;
}