UVa 01025
时隔3
个月后又开始了刷题,因为有用的书都在学校,所以只能再挑《算法竞赛入门经典(第2版)》中有用的题目来做一做了。
一条地铁线路有N
个车站,Maria从始发站上车,任务是在T
时刻在终点站会见一个间谍。因为这是秘密行动,如果在站台上等车时间过长,就会被特工逮捕,因此她必须尽可能少的停留在站台上,即使需要来回的乘车。
之前总结的动态规划解题方法分为3
步:
- 转换为分阶段决策的问题
- 寻找递推关系
- 合并重叠子问题
分阶段决策已经很明显了,直接找递推关系。如果Maria当前在第s
站,那么她肯定知道自己在站台上等待了多久。无论之前是以什么方式到达的第s
站,只要时间t
一样,都可以进行相同的选择(继续等车、往始发站乘车或者往终点站乘车),所以这种递推关系满足最优子结构和无后效性。最后合并重叠子问题,显然t
和s
相同时,只保留最少等待时间的状态即可,所以得到递推关系中的状态为waiting[t][s]
,表示时间t
在第s
站时已经在站台上累计等待的最小时间。
到这里这道题基本就结束了。由于t
表示的是时间,可以参考书上的代码,使用一个叫做has_train[t][s][2]
的三维数组,表示时间t
在车站s
是否有开向始发站和终点站的列车。但是这种方法太蠢了,毕竟不是每一个时间都有车,如果这样存储,会存在很多的时间t
,使得所有的车站都没有车,产生大量无用的数据,所以得把它优化掉,起码保证在时间t
,至少有一个车站有车。
如果只保留有意义的时间t
,那么waiting
的第1
维就不连续了,没法进行递推,所以用一个从时刻mom
到时间t
的映射viMoment
将其变成连续的,同时还有逆映射mTime
,最终得到的状态为waiting[mom][sta]
,表示时刻mom
在第sta
站时累计等待的最小时间。
这种表示方式的递推公式不好表示,直接看代码吧。
#include <iostream>
#include <vector>
#include <array>
#include <map>
#include <climits>
#define LEFT 0
#define RIGHT 1
using namespace std;
void buildTimeTable(map<int, vector<array<bool, 2>>> &mTimeTable, const vector<int> &viTravel, const vector<int> &viLeft, const vector<int> &viRight)
{
size_t stations = viTravel.size() + 1;
for (auto ldepart : viLeft)
{
if (mTimeTable.find(ldepart) == mTimeTable.end()) {
mTimeTable[ldepart] = vector<array<bool, 2>>(stations, { false, false });
}
mTimeTable[ldepart][0][RIGHT] = true;
for (size_t t = 0; t < viTravel.size(); t++)
{
ldepart += viTravel[t];
if (mTimeTable.find(ldepart) == mTimeTable.end()) {
mTimeTable[ldepart] = vector<array<bool, 2>>(stations, { false, false });
}
mTimeTable[ldepart][t + 1][RIGHT] = true;
}
}
for (auto rdepart : viRight)
{
if (mTimeTable.find(rdepart) == mTimeTable.end()) {
mTimeTable[rdepart] = vector<array<bool, 2>>(stations, { false, false });
}
mTimeTable[rdepart][stations - 1][LEFT] = true;
for (size_t t = viTravel.size(); t > 0; t--)
{
rdepart += viTravel[t - 1];
if (mTimeTable.find(rdepart) == mTimeTable.end()) {
mTimeTable[rdepart] = vector<array<bool, 2>>(stations, { false, false });
}
mTimeTable[rdepart][t - 1][LEFT] = true;
}
}
}
int calWaitingTime(const map<int, vector<array<bool, 2>>> &mTimeTable, const vector<int> &viTravel, int T)
{
map<int, size_t> mTime;
vector<int> viMoment;
size_t stations = viTravel.size() + 1;
for (auto iter = mTimeTable.begin(); iter != mTimeTable.end(); iter++)
{
mTime[iter->first] = viMoment.size();
viMoment.push_back(iter->first);
}
vector<vector<int>> waiting(viMoment.size(), vector<int>(stations, INT_MAX));
for (size_t mom = 0; mom < viMoment.size(); mom++)
{
waiting[mom][0] = viMoment[mom];
}
for (size_t mom = 0; mom < viMoment.size() - 1; mom++)
{
int now = viMoment[mom], stay = viMoment[mom + 1] - now;
size_t nextMoment;
for (size_t sta = 0; sta < stations; sta++)
{
if (waiting[mom][sta] == INT_MAX) continue;
if (sta != stations - 1 && mTimeTable.at(now)[sta][RIGHT]) {
nextMoment = mTime[now + viTravel[sta]];
if (waiting[nextMoment][sta + 1] > waiting[mom][sta]) {
waiting[nextMoment][sta + 1] = waiting[mom][sta];
}
}
if (sta != 0 && mTimeTable.at(now)[sta][LEFT]) {
nextMoment = mTime[now + viTravel[sta - 1]];
if (waiting[nextMoment][sta - 1] > waiting[mom][sta]) {
waiting[nextMoment][sta - 1] = waiting[mom][sta];
}
}
if (waiting[mom + 1][sta] > waiting[mom][sta] + stay) {
waiting[mom + 1][sta] = waiting[mom][sta] + stay;
}
}
}
for (size_t mom = viMoment.size(); mom > 0; mom--)
{
if (waiting[mom - 1][stations - 1] != INT_MAX && viMoment[mom - 1] <= T) {
return T - viMoment[mom - 1] + waiting[mom - 1][stations - 1];
}
}
return -1;
}
int main()
{
int Test = 0;
while (1) {
int N = 0, T = 0, M1 = 0, M2 = 0;
cin >> N;
if (N == 0) break;
cin >> T;
vector<int> viTravel(N - 1, 0);
for (int t = 0; t < N - 1; t++)
{
cin >> viTravel[t];
}
cin >> M1;
vector<int> viLeft(M1, 0);
for (int m = 0; m < M1; m++)
{
cin >> viLeft[m];
}
cin >> M2;
vector<int> viRight(M2, 0);
for (int m = 0; m < M2; m++)
{
cin >> viRight[m];
}
map<int, vector<array<bool, 2>>> mTimeTable;
buildTimeTable(mTimeTable, viTravel, viLeft, viRight);
int WaitingTime = calWaitingTime(mTimeTable, viTravel, T);
cout << "Case Number " << ++Test << ": ";
if (WaitingTime == -1)cout << "impossible" << endl;
else cout << WaitingTime << endl;
}
return 0;
}
/*
4
55
5 10 15
4
0 5 10 20
4
0 5 10 15
4
18
1 2 3
5
0 3 6 10 12
6
0 3 5 7 12 15
2
30
20
1
20
7
1 3 5 7 11 13 17
0
*/