1131 Subway Map(两种解法)

题目大意

给出各地铁线所经过的站点,构成一张地铁交通图。再给出起点和终点,让你找出最快的一条路径,如果路径不唯一就选择中转次数最少的那一条。

思路解析

本题是铁了心的要考邻接表。传统的Dijkstra + DFS最后一个测试点会超时(末尾也会附上该解法)。本题的难点是判断中转站和DFS以及地铁车次的记录。下面分三点讲解:
1. 邻接表所采用的最短路径法就是暴力遍历,用深搜从头走到尾,也就是说在DFS过程中得出最短路径。不同于邻接矩阵,邻接表沿着当前节点的出度逐项顺移,所以不同路径的相同节点必然会被遍历多次,但是要保证本次遍历不能兜圈子,所以visited具有本次有效性。
2. 由于需要统计中转站,所以每个节点必须要记录所通车次,所以需要用map记录,考虑到中转站所以要结合前一个节点综合得出key,value对应车次。
3. 中转站的判断要通过最终路径res得出,用O(n)的循环判断,i从1开始,判断res[i-1]→res[i]的车次是否和res[i-2]到res[i-1]的车次一致,可以把后者的车次计做preLine.每一次用res[i-1]→res[i]和preLine比较,不同则说明换站。

示例代码

#include<iostream>
#include<vector>
#include<map>
#define INF (~(0x1<<31))
using namespace std;
vector<vector<int>> gra(10000, vector<int>());//邻接矩阵存储
map<int, int> line;
int visited[10000];
int start, end1, minsize = INF,mincnt = INF;
vector<int> temp, res;
void dfs(int v) {//根据图顺移,每个点必然遍历多次,但要保证本次遍历不能出现环路,所以visited具有本次有效性
	if (v == end1 && temp.size() <= minsize ) {
		int preLine = -1, cnt = 0;
		for (int i = 1; i < temp.size(); i++) {//统计中转站
			if (line[temp[i - 1] * 10000 + temp[i]] != preLine) cnt++;
			preLine = line[temp[i - 1] * 10000 + temp[i]];
		}
		if (temp.size() < minsize) {
			minsize = temp.size();
			mincnt = cnt;
			res = temp;
		}
		else if (temp.size() == minsize && cnt < mincnt) {
			mincnt = cnt;
			res = temp;
		}
		return;
	}
	if (v == end1) return;
	for (int i = 0; i < gra[v].size(); i++) {
		if (visited[gra[v][i]] == 0) {
			visited[gra[v][i]] = 1;
			temp.push_back(gra[v][i]);
			dfs(gra[v][i]);
			temp.pop_back();
			visited[gra[v][i]] = 0;//本次遍历结束,下一条路径有可能还会用到
		}
	}
}
int main() {
	int n, k;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		int m, pre, temp;
		scanf("%d %d", &m, &pre);
		for (int j = 1; j < m; j++) {
			scanf("%d", &temp);
			gra[pre].push_back(temp);
			gra[temp].push_back(pre);
			line[pre * 10000 + temp] = line[temp * 10000 + pre] = i;
			pre = temp;
		}
	}
	scanf("%d", &k);
	for (int i = 0; i < k; i++) {
		scanf("%d %d", &start, &end1);
		mincnt = minsize = INF;
		visited[start] = 1;
		temp.clear();
		temp.push_back(start);
		dfs(start);
		visited[start] = 0;//不要忘记擦屁股
		printf("%d\n", res.size() - 1);
		int preLine = -1, start = res[0];
		for (int j = 1; j < res.size(); j++) {
			if (line[res[j - 1] * 10000 + res[j]] != preLine) {
				if (preLine != -1) 
					printf("Take Line#%d from %04d to %04d.\n", preLine, start, res[j-1]);
				preLine = line[res[j - 1] * 10000 + res[j]];
				start = res[j - 1];
			}
		}
		printf("Take Line#%d from %04d to %04d.\n", preLine, start, end1);
	}
	return 0;
}

Dijkstra解法(最后一个测试点超时,但最好还是掌握该法)

为了避免内存超限所以使用了unordered_map,然并卵,该超时的还是超时,unordered_map相比map仅仅是空间上节省了,但由于缺少了红黑树的支持,在查找上会不足。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<unordered_map>
#include<algorithm>
#define INF (~(0x1<<31))
using namespace std;
int n, m, num = 0;
int gra[10000][10000];
unordered_map<int, int> line;//记录车站通行的车次
vector<vector<int>> dijkstra(int s) {
	vector<bool> visited(num,false);
	vector<int> dist(num, INF);
	vector<vector<int>> path(num);
	dist[s] = 0;
	for (int i = 0; i < num; i++) {
		int min = INF, u = -1;
		for (int j = 0; j < num; j++) {
			if (!visited[j] && dist[j] < min) {
				min = dist[j];
				u = j;
			}
		}
		if (u != -1) {
			visited[u] = true;
		}
		for (int j = 0; j < num; j++) {
			if (!visited[j] && gra[u][j] != INF) {
				if (dist[u] + gra[u][j] < dist[j]) {
					path[j].clear();
					path[j].push_back(u);
					dist[j] = dist[u] + gra[u][j];
				}
				else if (dist[u] + gra[u][j] == dist[j]) {
					path[j].push_back(u);
				}
			}
		}
	}	
	return path;
}
int s, mincnt = INF;//s作为起始点
vector<int> temp,res;
void dfs(int v, vector<vector<int>> path) {
	temp.push_back(v);
	if (v == s) {
		int cnt = -1, preLine = 0;
		for (int i = 1; i < temp.size(); i++) {
			if (line[temp[i - 1] * 10000 + temp[i]] != preLine) cnt++;
			preLine = line[temp[i - 1] * 10000 + temp[i]];
		}
		if (cnt < mincnt) {
			mincnt = cnt;
			res = temp;
		}
		temp.pop_back();
		return;
	}
	for (int i = 0; i < path[v].size(); i++) {
		dfs(path[v][i], path);
	}
	temp.pop_back();
}
unordered_map<int, int> mapp;//通过四位索引换算序号
unordered_map<int, int> rmapp;//供输出换算
int main() {
	scanf("%d", &n);
	fill(gra[0], gra[0] + 1010 * 1010, INF);	
	for (int i = 1; i <= n; i++) {
		scanf("%d", &m);
		int a;
		scanf("%d", &a);
		if (mapp.find(a) == mapp.end()) {
			rmapp[num] = a;
			mapp[a] = num++;
		}
		a = mapp[a];
		for (int j = 1; j < m; j++) {
			int b;
			scanf("%d", &b);
			if (mapp.find(b) == mapp.end()) {
				rmapp[num] = b;
				mapp[b] = num++;
			}
			b = mapp[b];
			line[a * 10000 + b] = line[b * 10000 + a] = i;
			gra[a][b] = gra[b][a] = 1;
			a = b;
		}
	}		
	int k;
	scanf("%d", &k);
	for (int i = 0; i < k; i++) {
		int a, b;
		scanf("%d %d", &a, &b);
		a = mapp[a];
		b = mapp[b];
		vector<vector<int>> path = dijkstra(a);
		s = a;
		res.clear();
		mincnt = INF;
		dfs(b, path);//从终点倒着回溯
		printf("%d\n", res.size() - 1);
		int preLine = -1, start = res[res.size() - 1];
		for (int j = res.size() - 2; j >= 0; j--) {
			if (line[res[j + 1] * 10000 + res[j]] != preLine) {
				if (preLine != -1) {
					printf("Take Line#%d from %04d to %04d.\n", preLine, rmapp[start], rmapp[res[j + 1]]);
				}
				preLine = line[res[j + 1] * 10000 + res[j]];
				start = res[j + 1];
			}
		}
		printf("Take Line#%d from %04d to %04d.\n", preLine, rmapp[start], rmapp[b]);
	}
	return 0;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值