1131 Subway Map (30 分)

44 篇文章 0 订阅

题目大意:给定一个无向图并带有编号,给定两个点,求两点最短路径,如果存在多条最短路径,要求取换乘最少的路径。

单源最短路径,权值为1可以直接bfs,也可以dfs+剪枝或者dijkstra,这里dijkstra需要堆优化。

本题难就难如何建图上,要保存两点之间的路线编号,可以用偏移量存储两点的编号,也可以直接邻接表同步信息。

下面提供dfs和dijkstra版本的代码

dfs

#include <iostream>
#include <queue>
#include <cstring>
#include <unordered_map>
using namespace std;
const int N = 10010;
vector<int>e[N],ans,res;
unordered_map<int,int>mp;
int a,b,dis,last;
bool vis[N];
void dfs(int st,int en,int pre,int cnt) //cnt为换乘次数,pre为上一个点
{    
	if(dis < ans.size()) return ; //剪枝 dis最短路径
	if(dis == ans.size() && cnt > last) return ; //剪枝 last当前最短路径的换乘次数
	if(st == en)
	{   
	    dis = ans.size();
		res = ans;last = cnt;
		return ;
	}
	for(auto it : e[st])
	{
		if(vis[it]) continue;
		ans.push_back(it),vis[it] = true;
		int x = mp[st * N + it] == mp[pre * N + st] ? 0 : 1;//判断是否换乘,pre记录上一个点
		dfs(it,en,st,cnt + x);
		ans.pop_back();
		vis[it] = false;
	}
}
void solve()
{
	int st,en;
	cin >> st >> en;
	dis = 1e9,last = 1e9;
	ans.clear();ans.push_back(st);vis[st] = true;
	dfs(st,en,-1,0);vis[st] = false;
	cout << res.size() - 1 << endl;
	int pre = 0;
	for(int i = 1; i < res.size(); i ++)
	{
	    if(mp[res[i - 1] * N + res[i]]  != pre)
		{    
			if(pre != 0) printf("Take Line#%d from %04d to %04d.\n",pre,st,res[i - 1]);
            pre = mp[res[i - 1] * N + res[i]];st = res[i - 1];
		}
	}
	printf("Take Line#%d from %04d to %04d.\n",pre,st,en);
}
int main()
{
	int tt;
	cin >> tt;
	for(int k = 1;k <= tt; k ++)
	{
		int m;
		cin >> m >> a;
		for(int i = 0; i < m - 1; i ++)
		{   
			cin >> b;
			e[a].push_back(b),e[b].push_back(a);//无向边
			mp[a * N + b] = mp[b * N + a] = k;//相邻两点保存路线标号
			a = b;
		}
	}
	int T;
	cin >> T;
	while(T --) solve();
	return 0;
}

dijkstra堆优化

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int,int> pii;
const int N = 10010,M = 1000010;
int n,m,a,b,tt,T;
int e[M],h[N],ne[M],w[M],line[M],dis[N],cnt[N],p[N],idx;// line保存的是编号
bool st[N];
void add(int a,int b,int c,int d)
{
	e[idx] = b,w[idx] = c,ne[idx] = h[a],line[idx] = d,h[a] = idx ++;
}
string str[N];
string getzero(int  x)
{
	string s = to_string(x);
	while(s.size() < 4) s = "0" + s;
	return s;
}
void dijkstra(int s,int en)
{
	memset(dis,0x3f,sizeof dis);
	memset(cnt,0,sizeof cnt);
	memset(st,false,sizeof st);
	dis[s] = 0;
	priority_queue<pii,vector<pii>,greater<pii>>que;
	que.push({0,s});
	while(que.size())
	{
		auto t = que.top();que.pop();
		int u = t.second;
		if(st[u]) continue;
		if(u == en) break;//说明到终点的点已经更新完了
		st[u] = true;
		for(int i = h[u]; ~i; i = ne[i])
		{
			int j = e[i];
			if(dis[j] > dis[u] + w[i])
			{
				dis[j] = dis[u] + w[i];
				cnt[j] = cnt[u];//换乘次数
				p[j] = u;;//保存结果路径
				que.push({dis[j],j});
				str[j] = "Take Line#"+ to_string(line[i]) 
                +" from "+ getzero(u) + " to " + getzero(j) + ".";
			}
			else if(dis[j] == dis[u] + w[i] && cnt[j] > cnt[u] + 1)
			{
				cnt[j] = cnt[u] + 1;
				p[j] = u;
				str[j] = "Take Line#"+ to_string(line[i]) 
                +" from "+ getzero(u) + " to " + getzero(j) + ".";
			}
		}
	}
	cout << dis[en] << '\n';
	vector<string>res;
	for(int i = en; i != s; i = p[i])  res.push_back(str[i]);//获取路径
	for(int i = res.size() - 1; i >= 0; i --) cout << res[i] << '\n';

}
int main()
{   
	cin >> tt;
	memset(h,-1,sizeof h);
	for(int k = 1; k <= tt; k ++)
	{
		cin >> m;vector<int>v(m);
		for(int i = 0; i < m; i ++) cin >> v[i];
		for(int i = 0; i < m; i ++)
			for(int j = 0; j < i; j ++)
			{   
				int dist = i - j;
				if(v[0] == v[m - 1]) //如果是一个环路
					dist = min(dist,m - dist - 1);//无向图取最短
				add(v[i],v[j],dist,k),add(v[j],v[i],dist,k);
			}
	}
	cin >> T;
	while(T --)
	{
		int st,en;
		cin >> st >> en;
		dijkstra(st,en);
	}
	return 0;

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值