PAT A1131

题目:https://pintia.cn/problem-sets/994805342720868352/problems/994805347523346432

这个题思路说不上很难,但是在考试时间的限制下真的是压力山大,十万条命接不起。本人用的两次DFS,一次找最短路径。第二次通过最少的换乘数找最佳路径。感觉比较难的点,是保存几号线以及中转站。别忽略地铁是双向的。该算法要剪枝,不然要超时。因为地铁是双向的,所以第二个DFS找最佳路径时,可以把终点想成起点,然后乘坐几号线去某个中转站(或终点)。所以用一个结构体,记录这两个数据。

#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 10010;
const int INF = 1e9;
int n, m, k, st, ed;
bool vis[maxn] = {false};
int d[maxn], line[maxn][maxn]; //d记录最短路长,line记录a到b是几号线(其实a,b是临近的两站)。
vector<int> sta[maxn]; //记录该站可以通往哪些站
vector<int> pre[maxn]; //记录前驱路径

void DFS(int u){
	if(vis[u]){
		return;
	}
	vis[u] = true;
	for(int i = 0; i < sta[u].size(); i++){
		int v = sta[u][i];
		if(!vis[v]){
			d[v] = d[u] + 1;
			pre[v].push_back(u);
		}
		else{
			if(d[v] > d[u] + 1){
				d[v] = d[u] + 1;
				vis[v] = false;//为了更新之后的所有最短路长
				pre[v].clear();
				pre[v].push_back(u);
			}
			else if(d[v] == d[u] + 1)
				pre[v].push_back(u);
		}
		DFS(v);
	}
}


struct Node{          //用来记录中转站和几号线
	int trans;
	int take;
	Node (int a, int b) : trans(a), take(b){};
};
vector<Node> path, temp;
int now, minnum; //now是当前是几号线,minnum是最小换乘次数
void DFS2(int u, int num){
	if(u == st){
		if(num < minnum){
			minnum = num;
			path = temp;
		}
		return;
	}

	for(int i = 0; i < pre[u].size(); i++){
		int v = pre[u][i];
		if(u == ed){  //第一个点进行初始化
			temp.clear();
			now = line[u][v];
			temp.push_back(Node(u, now));
		}
		if(line[u][v] != now){
			if(num + 1 > minnum)  //剪枝
				continue;
			int oldnow = now; //oldnow和num一定要还原!!!
			now = line[u][v];
			temp.push_back(Node(u, now));
			DFS2(v, ++num);
			temp.pop_back();
			num--;
			now = oldnow;
		}
		else if(num < minnum){ //剪枝
			DFS2(v, num);
		}
	}
}
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i++){
		scanf("%d", &m);
		int pre;
		for(int j = 0; j < m; j++){
			int stop;
			scanf("%d", &stop);
			if(j){
				sta[pre].push_back(stop);
				sta[stop].push_back(pre);
				line[pre][stop] = i;
				line[stop][pre] = i;
			}
			pre = stop;
		}
	}
	scanf("%d", &k);
	for(int i = 0; i < k; i++){
		scanf("%d%d", &st, &ed);
		fill(vis, vis+maxn, 0);
		fill(d, d+maxn, INF);
		d[st] = 0;
		for(int j = 0; j < maxn; j++){//初始化上一次的前驱节点
			pre[j].clear();
		}
		DFS(st);
		printf("%d\n", d[ed]);
		minnum = INF;
		DFS2(ed, 0); 
		int s1, s2;
		for(int i = path.size() - 1; i >= 0; i--){
			if(i == path.size() - 1){
				s1 = st;
				s2 = path[i].trans;
			}else{
				s1 = path[i+1].trans;
				s2 = path[i].trans;
			}
			printf("Take Line#%d from %04d to %04d.\n", path[i].take, s1, s2);
		}
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值