题目大意:给定一个无向图并带有编号,给定两个点,求两点最短路径,如果存在多条最短路径,要求取换乘最少的路径。
单源最短路径,权值为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;
}