Programming Challenges 习题9.6.7

本文介绍了一个算法问题,目标是在夜间列车网络中找到从起点到终点所需的最小血量。通过广度优先搜索和优先队列,算法考虑了出发和到达时间的限制,以确保主角在夜间旅行中不会遇到危险。

PC/UVa:110907/10187

From Dusk Till Dawn

中文题目有问题,火车路线不是出发时间和到达时间,而是出发时间和旅行时间。

输入有几个要注意的点:

  • 网上有人说火车出发时间可能会大于24,需要注意对时间进行修正
  • 判断火车时间是否满足要求时,要严格使出发时间在18点到6点之间,并且到达时间在出发时间之后(uDebug上有一个例子中,出发时间4,旅行时间15,也就是到达时间19,单独判断时这两个时间都在合理范围内,但是并不合理)

因为从出发城市到目的城市可能存在多条路径,这道题要找血量最少的路径,找所有路径要用广搜。

广度优先搜索时要使用优先队列,否则会超时,优先队列中节点应该是按照血量排序的,然后才是途径城市最少的。

广搜的循环中,最开始写的是到达一个新的城市后,就判断是否到达目的城市。但是该点到达目标城市可能有多趟火车,在没有对火车时间排序的情况下,直接乘坐第一趟火车可能会多带一升血,所以正确的代码应该是全都加入优先队列中,直到这次取出的是目的城市。

#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <queue>
#include <climits>

using namespace std;

struct Train
{
	size_t dst;
	size_t start, arrive;
	Train(const size_t dst, const size_t start, const size_t arrive)
		:dst(dst), start(start), arrive(arrive){};
};

struct Node
{
	size_t city;
	size_t blood;
	vector<Train> path;
	vector<bool> vbVisit;
};

bool operator<(const Node &n1, const Node &n2)
{
	if (n1.blood > n2.blood) return true;
	else if (n1.blood < n2.blood) return false;
	else{
		if (n1.path.size() > n2.path.size()) return true;
		else if (n1.path.size() < n2.path.size()) return false;
		else{
			return n1.path[0].dst < n2.path[0].dst;
		}
	}
}

void buildGraph(vector<string> &vecCity, map<string, size_t> &mCity2ID, vector<vector<Train>> &Graph)
{
	int road = 0;
	cin >> road;
	cin.get();
	size_t ID = 0;
	string strCity1, strCity2;
	size_t start, duration, arrive, city1, city2;
	for (int r = 0; r < road; r++)
	{
		cin >> strCity1 >> strCity2;
		if (mCity2ID.find(strCity1) != mCity2ID.end()){
			city1 = mCity2ID[strCity1];
		}
		else{
			city1 = ID;
			vecCity.push_back(strCity1);
			mCity2ID[strCity1] = ID++;
			Graph.push_back(vector<Train>());
		}
		if (mCity2ID.find(strCity2) != mCity2ID.end()){
			city2 = mCity2ID[strCity2];
		}
		else{
			city2 = ID;
			vecCity.push_back(strCity2);
			mCity2ID[strCity2] = ID++;
			Graph.push_back(vector<Train>());
		}
		cin >> start >> duration;
		cin.get();
		arrive = start + duration;
		if (start >= 24) start -= 24;
		if (arrive >= 24) arrive -= 24;
		//if ((start > 6 && start < 18) || (arrive > 6 && arrive < 18)) continue;
		if (start >= 18 && (arrive > start || arrive <= 6) ||
			start < 6 && (arrive > start && arrive <= 6)){
			Graph[city1].push_back(Train(city2, start, arrive));
		}
	}
}

void printGraph(const vector<string> &vecCity, const map<string, size_t> &mCity2ID, const vector<vector<Train>> &Graph)
{
	for (size_t city = 0; city < Graph.size(); city++)
	{
		const vector<Train> &vecTrain = Graph[city];
		for (auto train : vecTrain)
		{
			//cout << vecCity[city] << " to " << vecCity[train.dst];
			cout << city << " to " << train.dst;
			cout << " starts at " << train.start << " and arrives at " << train.arrive << endl;
		}
	}
}

void findMinBlood(const vector<vector<Train>> &Graph, size_t CityStart, size_t CityArrive)
{
	//cout << "from " << CityStart << " to " << CityArrive << endl;
	size_t blood = UINT_MAX;
	priority_queue<Node> qNode;
	Node node, curr;
	node.city = CityStart;
	node.blood = 0;
	node.vbVisit.resize(Graph.size());
	node.vbVisit[CityStart] = true;
	qNode.push(node);
	while (!qNode.empty()){
		curr = qNode.top();
		qNode.pop();
		if (curr.city == CityArrive){
			blood = curr.blood;
			break;
		};
		const vector<Train> vecTrain = Graph[curr.city];
		for (auto train : vecTrain)
		{
			if (curr.vbVisit[train.dst]) continue;//路过的城市不返回
			node = curr;
			if (!node.path.empty()){//非出发城市,计算是否需要多带一升
				size_t arrive = node.path.back().arrive;
				if (arrive > 18){
					if (train.start >= arrive || train.start < 6);//再出发
					else node.blood++;
				}
				else {
					if (train.start >= arrive && train.start < 6);//再出发
					else node.blood++;
				}
			}
			node.city = train.dst;
			node.path.push_back(train);
			node.vbVisit[train.dst] = true;
			//当前点扩展后,如果到达目的地后不能直接退出
			//因为当前点可能用了额外的1升血
			//但是其它点扩展到目的地时可能不需要额外的1升血
			qNode.push(node);
		}
	}
	if (blood != UINT_MAX){
		cout << "Vladimir needs " << blood << " litre(s) of blood." << endl;
	}
	else{
		cout << "There is no route Vladimir can take." << endl;
	}
}

int main()
{
	int T = 0;
	cin >> T;
	cin.get();
	for (int t = 0; t < T; t++)
	{
		vector<string> vecCity;
		map<string, size_t> mCity2ID;
		vector<vector<Train>> Graph;
		buildGraph(vecCity, mCity2ID, Graph);
		string strStartCity, strArriveCity;
		cin >> strStartCity >> strArriveCity;
		//printGraph(vecCity, mCity2ID, Graph);
		cout << "Test Case " << t + 1 << '.' << endl;
		if (strStartCity == strArriveCity){
			cout << "Vladimir needs 0 litre(s) of blood." << endl;
		}
		else if (mCity2ID.find(strStartCity) == mCity2ID.end() ||
			mCity2ID.find(strArriveCity) == mCity2ID.end()){
			cout << "There is no route Vladimir can take." << endl;
		}
		else findMinBlood(Graph, mCity2ID[strStartCity], mCity2ID[strArriveCity]);
	}
	return 0;
}
/*
2
3
Ulm Muenchen 17 2
Ulm Muenchen 19 12
Ulm Muenchen 5 2
Ulm Muenchen
10
Lugoj Sibiu 12 6
Lugoj Sibiu 18 6
Lugoj Sibiu 24 5
Lugoj Medias 22 8
Lugoj Medias 18 8
Lugoj Reghin 17 4
Sibiu Reghin 19 9
Sibiu Medias 20 3
Reghin Medias 20 4
Reghin Bacau 24 6
Lugoj Bacau
*/

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值