算法竞赛入门经典 例题9-1

UVa 01025

A Spy in the Metro

时隔3个月后又开始了刷题,因为有用的书都在学校,所以只能再挑《算法竞赛入门经典(第2版)》中有用的题目来做一做了。

一条地铁线路有N个车站,Maria从始发站上车,任务是在T时刻在终点站会见一个间谍。因为这是秘密行动,如果在站台上等车时间过长,就会被特工逮捕,因此她必须尽可能少的停留在站台上,即使需要来回的乘车。

之前总结的动态规划解题方法分为3步:

  • 转换为分阶段决策的问题
  • 寻找递推关系
  • 合并重叠子问题

分阶段决策已经很明显了,直接找递推关系。如果Maria当前在第s站,那么她肯定知道自己在站台上等待了多久。无论之前是以什么方式到达的第s站,只要时间t一样,都可以进行相同的选择(继续等车、往始发站乘车或者往终点站乘车),所以这种递推关系满足最优子结构和无后效性。最后合并重叠子问题,显然ts相同时,只保留最少等待时间的状态即可,所以得到递推关系中的状态为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
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值