PAT甲级1003 Dijkstra拓展最短路径的数量+最大权值的最短路径

题目链接:传送门

万万没想到啊!第三题就把我难住了,果然是PAT教我做人,大概了解甲级的难度了,好好刷题吧我个呆瓜
是我太笨了,想不到这么巧妙的方法,说出来呢其实就是迪杰斯特拉单源最短路径算法的一点扩展和变种,可能没见过所以就不会做!!!!!!

哦对,这题不是我自己做的,是看了大佬的博客,以下是链接,自取自取:传送门
但其实大佬没有过多的解释,建议看不懂代码的可以看看我的解释!!!(卑微

这里是迪杰斯特拉算法的大致过程,心里明白的可以不用看, 不明白的可以大致看下复习下
(写的非常粗糙,不适合新手学习观看)
是这样,迪杰斯特拉算法的过程大概就是
①dist数组里面存储从起点到其他所有点的最短距离。dist[i]表示从源点 s 到 i 的最短距离,初始化为图上的边权,从 s 到 i 有边则初始化为边权,无边则初始化为无穷大。
②在所有未被访问过的点中选择出dist值最小的点k ③将k点加入到最小路径权值已知的点集合中来(代码中表现为将其标记为一个已访问过的点)
④更新。更新什么呢?因为我们刚刚将一个新的点k加入到了最小路径权值已知的集合中来,那因为这个k之前是没有的,那是不是有可能存在从源点 s 到 k ,再从 k 到某个点 i 的距离会因为k这个点的加入距离变小呢,答案是有可能的,所以要对所有未被访问过的点进行检查更新,如果能够通过k缩短路径,就要更新dist数组的值。

这样每次选出一个dist最小的值,选n-1次,就可以把整个图全覆盖,完成单源最短路径算法。

这道题目的变形在于,可以利用更新过程完成这道题目所需要的到的信息。
这道题目需要得到的信息是:从源点S到终点T的最短路径有几条,这些路径中,权值最大的是多少(一条路径的权值为该条路径上所有点权值之和)。

①要统计最短路径的条数,则需要用到num数组统计从源点到点 i 的最短路径条数,我们想想最短路径是不是在更新的时候,可能会更新得到更短的路径,那每一次更新都是通过dist值最小的点 K 来更新,那我们想想,从源点 S 到被更新的点 i 的最短路径数,是不是就等于从源点 S 到点 K 的最短路径数。相当于num[i]=num[k]。
原因就是,从s到k有 num[k]条最短路,从k到i有一条路(就是从k到i的边嘛),问从s到i有多少条最短路?很简单就是1*num[k]=num[k]。所以得到以上结论!

②要统计这些路径中哪条路径权值最大。
方法跟上面很像,就是在每次更新路径的时候把存储路径权值的数组val也一并更新,更新为val[k]+a[i] (a[i]代表的是点权,val[i]代表的是从源点到 i 的最小路径的最大权值)。

但是!!有一个问题需要注意!!
迪杰斯特拉的时候,只有路径长度变小我们才需要更新,因为我们当时只关心长度,但是现在不一样了,我们还需要在路径长度相同的情况下选择权值更大的,所以要加一句,当路径长度相等的时候,判断一下权值是否变大了,如果变大了,就更新为当前的权值,同时num也要加上num[k],因为路径相等说明,以k为中介到达i也是最短路径,所以要把num[k]这么多种路径数加上去,即num[i]+=num[k]。

这里num数组的处理不同于上面的是因为上面是我发现了更短的路径,那之前的num根本就不能再用了,因为他不是最小路径了,所以直接赋值,把之前的路径数覆盖掉,这里不一样,这里是,我现在也是最短路径数,我是答案的一部分,所以要跟之前的num合在一起更新num。

啊,说了罗里吧嗦一大堆,我好lui

不说了,不懂的结合代码看看吧,溜了,多刷题多见见世面!

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<stack>
#define inf 4000000000000000000
using namespace std;
typedef long long ll;
const ll maxn=1e6+50;
const ll mod=1e9+7;
const double eps=1e-9;
ll a[550],val[550],num[550],mp[550][550],dist[550];
bool vis[550];

int main()
{
	ll n,m,c1,c2;
	scanf("%lld %lld %lld %lld",&n,&m,&c1,&c2);
	for(ll i=0;i<n;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(ll i=0;i<=n;i++)
	{
		for(ll j=0;j<=n;j++)
		{
			mp[i][j]=4e18;
		}
	}
	for(ll i=1;i<=m;i++)
	{
		ll u,v,w;
		scanf("%lld %lld %lld",&u,&v,&w);
		mp[u][v]=w;
		mp[v][u]=w;
	}
	
	for(ll i=0;i<505;i++)
	{
		dist[i]=4e18;
	}
	dist[c1]=0;
	num[c1]=1;
	val[c1]=a[c1];
	
	
	for(ll o=1;o<=n;o++)
	{
		ll minn=4e18,k=-1;
		for(ll i=0;i<n;i++)
		{
			if(!vis[i]&&minn>dist[i])
			{
				minn=dist[i];
				k=i;
			}
		}
		if(k==-1)break;
		vis[k]=1;
		for(ll i=0;i<n;i++)
		{
			if(mp[k][i]!=inf&&!vis[i])
			{
				if(dist[i]>dist[k]+mp[k][i])
				{
					dist[i]=dist[k]+mp[k][i];
					num[i]=num[k];
					val[i]=val[k]+a[i];

				}
				else if(dist[i]==dist[k]+mp[k][i])
				{
					if(val[k]+a[i]>val[i])
					{
						val[i]=val[k]+a[i];
					}
					num[i]+=num[k];
				}
			}
		}
	}
	printf("%lld %lld\n",num[c2],val[c2]);
	
	//scanf("%lld",&n);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章