PAT 1131 Subway Map




这一题的大意是给出了一个地铁站的线路图,让我们找从某一个站点出发到另外一个站点的最短距离,并且换乘次数最少。

找最短距离直接用dijkstra即可,是容易的,这一题的难点在于如何找最少的换乘次数,我刚开始用的是堆优化的dijkstra+dfs来找到每一条路径然后再从中找出最少的换乘次数,这样写很麻烦,我没有写出来如何找最少的换乘次数。

之后我借鉴了y总的思路,在建图的时候进行处理优化即可做出来。

如何进行优化呢?

我们知道这一题需要多次换乘,我们很难用代码来写某一站的时候确定换乘哪一个线路,因为一个站可以换乘多个线路。

我们可以选择在建图的时候多建些边,什么样呢?

原本:

现在:

我们在原来的基础上,对每一条线路,建任意两个站直达的边,这样我们就可以避免讨论换乘点了。

这一点是很巧妙的

剩下的就是用dijkstra来找最短路径了,需要注意的是在普通的堆优化的dijkstra的基础上增加了一个条件就是:
当最短路径相同时,优先通过站数少的,即有些最短路径可能相同,但可以直达的直接选择直达就好。

实际上这个条件就是让我们当在同一条路径上选择可以直达的(因为我们在建图的过程中,在每一条线路上都已经对每一个点都进行了建直达的边 )。

最后我们用一个字符串来保存在一条线路上的信息,把所有这样的信息放入字符串数组中最后输出结果即可。

完整代码如下:

cpp 复制代码
#include <iostream>
#include <limits.h>
#include <cstring>
#include <queue>
#include <unordered_map>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
using namespace std;
int N;
int M; 
int head[10005];
int dist[10005];
bool flag[10005];
typedef pair<int,int> pii;
int cnt[10005];
priority_queue<pii,vector<pii>,greater<pii>> q;
struct node
{
	int w;
	int next;
	int to;
	int line;
}e[1000005];
int cut;
int pre[10005];
string info[10005];
void add(int x,int y,int w,int line)
{
	e[cut].w=w;
	e[cut].line=line;
	e[cut].to=y;
	e[cut].next=head[x];
	head[x]=cut;
	cut++;
}
string getnumber(int x)
{
	string s=to_string(x);
	while(s.size()<4)
	{
		s='0'+s;
	}
	return s;
}
void dijkstra(int s,int end)
{
	memset(dist,0x3f,sizeof(dist));
	memset(flag,0,sizeof(flag));
	memset(cnt,0x3f,sizeof(cnt));
	dist[s]=0;
	cnt[s]=0;
	q.push({dist[s],s});
	while(!q.empty())
	{
		pii z=q.top();
		q.pop();
		int u=z.second;
		if(flag[u]==0)
		{
			flag[u]=1; 
			for(int i=head[u];i!=-1;i=e[i].next)
			{
				int v=e[i].to;
				if(flag[v]==0&&dist[v]>dist[u]+e[i].w)
				{
					dist[v]=dist[u]+e[i].w;
					cnt[v]=cnt[u]+1;
					pre[v]=u;
					info[v]="Take Line#"+to_string(e[i].line)+" from "+getnumber(u)+" to "+getnumber(v)+".";
					q.push({dist[v],v});
				}
				else if(dist[v]==dist[u]+e[i].w)
				{
					if(cnt[v]>cnt[u]+1)
					{
						cnt[v]=cnt[u]+1;
						pre[v]=u;
						info[v]="Take Line#"+to_string(e[i].line)+" from "+getnumber(u)+" to "+getnumber(v)+".";
					} 
				}
			}
		}
		else
		{
			continue;
		}
	}
	cout<<dist[end]<<endl;
	vector<string> ans;
	for(int i=end;i!=s;i=pre[i])
	{
		ans.push_back(info[i]);
	}
	for(int i=ans.size()-1;i>=0;i--)
	{
		cout<<ans[i]<<endl;
	}
	
}
int main()
{
	//ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>N;
    memset(head,-1,sizeof(head)); 
    for(int i=0;i<N;i++)
    {
    	cin>>M;
    	vector<int> stop;
    	for(int j=0;j<M;j++)
    	{
    		int x;
    		cin>>x;
    		stop.push_back(x);
		}
		for(int j=0;j<M;j++)
		{
			for(int k=0;k<j;k++)
			{
				//判断这两个点的距离也就是w这个权值
				//注意可能有环,所以我们需要分情况讨论
				int w= INT_MAX;
				if(stop[0]==stop[M-1])
				{
				   //有环 
				   //找正反两条路的最小路 
				   w=min(j-k,k-0+M-1-j);
				} 
				else
				{
					//无环 
					w=j-k;
				}
				int line=i+1;
				add(stop[k],stop[j],w,line);
				add(stop[j],stop[k],w,line);
			}
		}
	}
	//这样就建好了图了  
	
	int m;
	cin>>m;
	for(int i=0;i<m;i++)
	{
		int start;
		int end;
		cin>>start>>end; 
		//cout<<"1"<<endl;
		dijkstra(start,end);
	    
	}
  
	return 0;
 } 

时间复杂度 点是10^4 边是10^6 ,边为什么是10^6呢 因为在一条线路上 原本能够建边的是10^2 ,又因为 在一条线路上任意两个点要建直达边, 故再乘上 10^2 ⇒ 10^4 ,而有N条路线 ,也即 10^4*10 ^ 2 ⇒ 10^6

dijkstra算法的时间复杂度大致为 O(10^6log10 ^ 4)⇒ k次询问 k=10 故总的时间复杂度为 10 ^ 7log10^4 是小于10^8的

相关推荐
csdn_aspnet42 分钟前
Python 算法快闪 LeetCode 编号 70 - 爬楼梯
python·算法·leetcode·职场和发展
m0_629494733 小时前
LeetCode 热题 100-----26.环形链表 II
数据结构·算法·leetcode·链表
壹号用户4 小时前
用队列实现栈
数据结构·算法
做人求其滴4 小时前
面试经典 150 题 380 274
c++·算法·面试·职场和发展·力扣
daad7774 小时前
记一组无人机IMU传感器数据
算法
计算机安禾4 小时前
【c++面向对象编程】第42篇:模板特化与偏特化:为特定类型定制实现
开发语言·c++·算法
小O的算法实验室4 小时前
2026年KBS,流形感知强化学习差分进化算法+不规则3D无人机路径规划,深度解析+性能实测
算法·智能算法·智能算法改进
玖釉-4 小时前
C++ 中的循环语句详解:while、do...while、for、嵌套循环与循环控制
开发语言·c++·算法
不做无法实现的梦~5 小时前
运动控制系统复习一览-----常考题目总结版本
算法