PAT (Advanced Level) 1131 Subway Map——堆优化Dijkstra+dfs

题目传送门

我认为,这道题的难点在于路径输出
我一开始的思路,是对每一个站点记录ta在哪一条地铁线上
这样就会带来一个问题:换乘点要如何处理?

我们换一个思路,从路径出发,记录到达每一个站点时经过了几号线
输出时只要发现这个站点的前后地铁线不同,就认为ta是一个换乘点
每发现一个换乘点,就输出一条路径信息
终点站也可以视为一个换乘点,我们将终点站之后的地铁线设置为0,就可以实现统一的输出了

面对这种最短最优路径问题,我首选dijkstra+dfs的方法,结果TLE了
使用朴素的dijkstra算法会超时: O ( n 2 ) = 1000 0 2 = 1 0 8 O(n^2)=10000^2= 10^8 O(n2)=100002=108
需要使用堆优化版本的dijkstra算法: O ( m × l o g n ) = 1 0 6 × l o g 10000 = 4 × 1 0 6 O(m×logn)=10^6×log10000=4×10^6 O(m×logn)=106×log10000=4×106

还有没有更简单的方法呢?
因为题目限定,每个换乘点至多有五条线路交汇
这实际上暗示了图的复杂度并不会太高
我们只需要一遍dfs即可

感谢前辈们的赛博指导:
堆优化dijkstra算法
柳神就是我的神!

Tip

  • 最短最优路径问题,复杂图使用dijkstra+dfs,简单图使用dfs
  • 学会转化问题,从不同角度解决问题
  • dfs用于搜索则有回溯,用于遍历则无回溯

TLE代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;

const int N = 10010;
const int INF = 0x33333333;
int n, m;
struct node {
	int y;
	int c;
	int nxt;
};
node way[N * 100];
int st[N], tot = -1;

void addway(int u, int v, int w) {
	tot++;
	way[tot].y = v;
	way[tot].c = w;
	way[tot].nxt = st[u];
	st[u] = tot;
}

int vis[N];
int dis[N];

int dijkstra(int s,int t) {
	for (int i = 0; i <= 9999; ++i) {
		vis[i] = 0;
		dis[i] = INF;
	}
	dis[s] = 0;
	while (1) {
		int mn = INF;
		int index = -1;
		for (int i = 0; i <= 9999; ++i) {
			if (!vis[i] && dis[i] < mn) {
				mn = dis[i];
				index = i;
			}
		}
		if (index == t || index == -1) break;
		vis[index] = 1;
		for (int i = st[index]; i != -1; i = way[i].nxt) {
			dis[way[i].y] = min(dis[way[i].y], dis[index] + 1);
		}
	}
	return dis[t];
}

int station[N];
int line[N];
int ans_station[N];
int ans_line[N];
int ans_cnt;

void dfs(int s,int t,int dis,int cnt,int ans) {
	if (dis > ans || (dis == ans && s!=t)) return;
	if (dis == ans && s == t) {
		if (cnt < ans_cnt) {
			ans_cnt = cnt;
			for (int i = 1; i <= dis; ++i) {
				ans_station[i] = station[i];
				ans_line[i] = line[i];
			}
		}
		return;
	}
	for (int i = st[s]; i != -1; i = way[i].nxt) {
		int y = way[i].y;
		if (!vis[y]) {
			//arrive station through line
			station[dis + 1] = y;
			line[dis + 1] = way[i].c;
			vis[y] = 1;
			//different line
			if (line[dis+1] != line[dis]) dfs(y, t, dis + 1, cnt + 1, ans);
			else dfs(y, t, dis + 1, cnt, ans);
			vis[y] = 0;
		}
	}
}

void prt(int s,int t,int ans) {
    //终点也可以视为换乘点
	ans_line[ans + 1] = 0;
	for (int i = 1; i <= ans; ++i) {
		if (ans_line[i] != ans_line[i+1]) {
		    //ans_station[i]为换乘点
			printf("Take Line#%d from %04d to %04d.\n", ans_line[i], s, ans_station[i]);
			s = ans_station[i];
		}
	}	
}

int main()
{
	for (int i = 0; i <= 9999; ++i) st[i] = -1;
	scanf("%d",&n);
	for (int i = 1; i <= n; ++i) {
		int u, v;
		scanf("%d%d", &m, &u);
		for (int j = 1; j < m; ++j) {
			scanf("%d", &v);
			addway(u, v, i);
			addway(v, u, i);
			u = v;
		}
	}
	scanf("%d", &m);
	for (int i = 1; i <= m; ++i) {
		int u, v;
		scanf("%d%d",&u,&v);
		int ans = dijkstra(u, v);
		printf("%d\n",ans);
		if (ans) {
			for (int j = 0; j <= 9999; ++j) vis[j] = 0;
			vis[u] = 1;
			ans_cnt = INF;
			dfs(u, v, 0, 0, ans);
			prt(u, v, ans);
		}
	}
	return 0;
}

AC代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;

const int N = 10010;
const int INF = 0x33333333;
int n, m;
struct node {
	int y;
	int c;
	int nxt;
};
node way[N * 100];
int st[N], tot = -1;

void addway(int u, int v, int w) {
	tot++;
	way[tot].y = v;
	way[tot].c = w;
	way[tot].nxt = st[u];
	st[u] = tot;
}

int vis[N];
int station[N];
int line[N];
int ans_station[N];
int ans_line[N];
int ans_cnt;
int ans_dis;

void dfs(int s,int t,int dis,int cnt) {
	if (dis > ans_dis) return;
	if (s == t && dis <= ans_dis) {
		if ((dis < ans_dis) || (dis == ans_dis && cnt < ans_cnt)) {
			ans_dis = dis;
			ans_cnt = cnt;
			for (int i = 1; i <= dis; ++i) {
				ans_station[i] = station[i];
				ans_line[i] = line[i];
			}
		}
		return;
	}
	for (int i = st[s]; i != -1; i = way[i].nxt) {
		int y = way[i].y;
		if (!vis[y]) {
			//arrive station through line
			station[dis + 1] = y;
			line[dis + 1] = way[i].c;
			vis[y] = 1;
			//different line
			if (line[dis+1] != line[dis]) dfs(y, t, dis + 1, cnt + 1);
			else dfs(y, t, dis + 1, cnt);
			vis[y] = 0;
		}
	}
}

void prt(int s,int t,int ans) {
    //终点也可以视为换乘点
	ans_line[ans + 1] = 0;
	for (int i = 1; i <= ans; ++i) {
		if (ans_line[i] != ans_line[i+1]) {
		    //ans_station[i]为换乘点
			printf("Take Line#%d from %04d to %04d.\n", ans_line[i], s, ans_station[i]);
			s = ans_station[i];
		}
	}	
}

int main()
{
	for (int i = 0; i <= 9999; ++i) st[i] = -1;
	scanf("%d",&n);
	for (int i = 1; i <= n; ++i) {
		int u, v;
		scanf("%d%d", &m, &u);
		for (int j = 1; j < m; ++j) {
			scanf("%d", &v);
			addway(u, v, i);
			addway(v, u, i);
			u = v;
		}
	}
	scanf("%d", &m);
	for (int i = 1; i <= m; ++i) {
		int u, v;
		scanf("%d%d",&u,&v);
		for (int j = 0; j <= 9999; ++j) vis[j] = 0;
		vis[u] = 1;
		ans_dis = INF;
		ans_cnt = INF;
		dfs(u, v, 0, 0);
		printf("%d\n", ans_dis);
		prt(u, v, ans_dis);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值