PTA L3-011 直捣黄龙(dijkstra)

题意

n个点,m条边,给出原点、终点。求从原点出发到终点的最短路,如果有不同的最短路径,就选择城市数(点)最多的,如果点数也一样,就选择路径上权重最大的。

输入样例

10 12 PAT DBY
DBY 100
PTA 20
PDS 90
PMS 40
TAP 50
ATP 200
LNN 80
LAO 30
LON 70
PAT PTA 10
PAT PMS 10
PAT ATP 20
PAT LNN 10
LNN LAO 10
LAO LON 10
LON DBY 10
PMS TAP 10
TAP DBY 10
DBY PDS 10
PDS PTA 10
DBY ATP 10

输出样例

PAT->PTA->PDS->DBY
3 30 210

思路

这道题记录数据的数组比较多,而且点用字符串给出,还要做两个map映射。

遇到了一个坑点,需引起我的注意。写最短路dijkstra时,我习惯一开始就把原点加入集合,设vis[0] = true, 打印路径对了,可答案输出的是 0 , 30 , 190 0,30,190 030190,第二个时间最短正确说明最短路的松弛是没问题的,可另外两个到达终点的最短路径数点权之和有问题(莫不是其他两个数组的更新逻辑出错了?调试了好久实在看不出就问度娘了,把别人的代码拼凑了一下,最后把vis[0] = true 这句去掉输出就正常了)

第一次打印的路径是247356819
第二次是0247356819

其实从答案也可以反推,路径数目累加不上去,点权少了第二个城市PTA的 20 20 20。因为这道题目的特殊性,原点的路径数是从 1 1 1开始的,到后面点路径如果想累加的话,一定要把原点包括进去,否则去更新road数组时就没数字去更新了。

总结

两者的区别是,第一次找最小点是不是原点。如果vis[0] = true,那么第一次能找到最小松弛点是原点。

#include<bits/stdc++.h>
using namespace std;
const int N = 502;
const int inf = 0x3f3f3f3f;
int n, m; 
int lnum, ldis, peo;
string source, dist;
map<string, int> mp1; //名字 to 数字 建图用 
map<int, string> mp2; // 数字 to 名字 打印路径用 
int g[N][N]; // 边权 
int dis[N];  // 距离 
int weight[N]; // 点权 
bool vis[N];
int path[N]; //前一个点
int road[N]; // 最快路径条数
int wnum[N]; // 路径点权重 
int point[N]; //路径点个数 

int findMin()
{
	int k = -1, mn = inf;
	for(int i = 0; i < n ; i++)
	{
		if(!vis[i] && mn > dis[i])
		{
			mn = dis[i];
			k = i;
		}
	}
	return k;
}
void dijk()
{
	memset(path, -1, sizeof(path));
	memset(dis, inf, sizeof(dis));
	memset(vis, false, sizeof(vis));
	for(int i = 0; i < n; i++)
		dis[i] = g[0][i];
	dis[0] = 0;
	//vis[0] = true; 
	road[0] = 1; // 这里要注意,在原点时算一条路
	
	while(1)
	{
		int tmp = findMin();
		if(tmp == -1) break;
		vis[tmp] = true;
		for(int i = 0; i < n; i++)
		{
			if (vis[i] || dis[i] < dis[tmp] + g[tmp][i]) continue;
			if(dis[i] > dis[tmp] + g[tmp][i])
			{
				dis[i] = dis[tmp] + g[tmp][i];
				path[i] = tmp;
				road[i] = 0; // 之前积累的最短路径数量状态被打破 
				point[i] = point[tmp] + 1;
				wnum[i] = wnum[tmp] + weight[i];	
			}
			else if(point[i] < point[tmp] + 1) // 时间相等时,选点最多的 
			{
				path[i] = tmp;
				point[i] = point[tmp] + 1;
				wnum[i] = wnum[tmp] + weight[i];	
			} // 点也相等,选路径权重最大 
			else if(point[i] == point[tmp] + 1 && wnum[i] < wnum[tmp] + weight[i])
			{
				path[i] = tmp;
				wnum[i] = wnum[tmp] + weight[i];	
			}
			road[i] += road[tmp]; // 三种情况都会经过 tmp 点
		}
	}
}
void printPath(int x)
{
	if(x != 0)
	{
		printPath(path[x]);
		cout << "->" << mp2[x];
	}
}
int main() 
{
	string s, t;
	int x;
	cin >> n >> m >> source >> dist;
	mp1[source] = 0; 
	mp2[0] = source;

	for(int i = 1; i < n; i++)
	{
		cin >> s >> x;
		mp1[s] = i; 
		mp2[i] = s;
		weight[i] = x;
	}
	
	memset(g, inf, sizeof(g));
	while(m--)
	{
		cin >> s >> t >> x;
		g[mp1[s]][mp1[t]] = x;
		g[mp1[t]][mp1[s]] = x;
	}
	dijk();
	int End = mp1[dist];
	cout << source; 
	printPath(End); 
	
	printf("\n%d %d %d\n", road[End], dis[End], wnum[End]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值