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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章