1. 程式人生 > >【Luogu1608】路徑統計(最短路)(DP)

【Luogu1608】路徑統計(最短路)(DP)

要花 include dijkstra main std mar 行為 不能 總數

題目傳送門

題目描述

“RP餐廳”的員工素質就是不一般,在齊刷刷的算出同一個電話號碼之後,就準備讓HZH,TZY去送快餐了,他們將自己居住的城市畫了一張地圖,已知在他們的地圖上,有N個地方,而且他們目前處在標註為“1”的小鎮上,而送餐的地點在標註為“N”的小鎮。(有點廢話)除此之外還知道這些道路都是單向的,從小鎮I到J需要花費D[I,J]的時間,為了更高效快捷的將快餐送到顧客手中,

他們想走一條從小鎮1到小鎮N花費最少的一條路,但是他們臨出發前,撞到因為在路上堵車而生氣的FYY,深受啟發,不能僅知道一條路線,萬一。。。,於是,他們邀請FYY一起來研究起了下一個問題:這個最少花費的路徑有多少條?

輸入格式

輸入文件第一行為兩個空格隔開的數N,E,表示這張地圖裏有多少個小鎮及有多少邊的信息。

下面E行,每行三個數I、J、C,表示從I小鎮到J小鎮有道路相連且花費為C.(註意,數據提供的邊信息可能會重復,不過保證I<>J,1<=I,J<=n)。

輸出格式

輸出文件包含兩個數,分別是最少花費和花費最少的路徑的總數.

兩個不同的最短路方案要求:路徑長度相同(均為最短路長度)且至少有一條邊不重合。

若城市N無法到達則只輸出一個(‘No answer’);

輸入樣例

5 4 1 5 4 1 2 2 2 5 2 4 1 1

輸出樣例

4 2

題解 & 吐槽

這道題是一個很經典的最短路計數問題。

第一問可以直接SPFA/Dijkstra跑出來。

第二問是DP。

但是如果第二問用遞推來搞的話。。。需要先跑一遍拓撲排序呀。。。太麻煩了呀。。。

所以我們可以采用記憶化搜索。

Code

#include <bits/stdc++.h>

using namespace std;

const int max_n=2000+5,max_m=4000000+5;

int n,m,total,ans;
int edge[max_n][max_n],dp[max_n],dis[max_n],book[max_n];

inline void SPFA()
{
	int from,to,w;
	queue <int> q;
	dis[1]=0;
	q.push(1);
	book[1]=1;
	while(!q.empty())
	{
		from=q.front();
		q.pop();
		book[from]=1;
		for(register int i=1;i<=n;++i)
		{
			to=i;
			w=edge[from][to];
			if(dis[to]>dis[from]+w)
			{
				dis[to]=dis[from]+w;
				if(!book[to])
				{
					book[to]=1;
					q.push(to);
				}
			}
		}
	}
	return;
}

inline int DP(int x)
{
	if(dp[x])
	{
		return dp[x];
	}
	if(x==1)
	{
		return dp[x]=1;
	}
	for(register int i=1;i<=n;++i)
	{
		if(dis[x]==dis[i]+edge[i][x])
		{
			dp[x]+=DP(i);
		}
	}
	return dp[x];
}

int main()
{
	memset(dis,0x3f,sizeof(dis));
	memset(edge,0x3f,sizeof(edge));
	int x,y,z;
	scanf("%d %d",&n,&m);
	for(register int i=1;i<=m;++i)
	{
		scanf("%d %d %d",&x,&y,&z);
		edge[x][y]=min(edge[x][y],z);	
	}
	SPFA();
	DP(n);
	if(dis[n]^0x3f3f3f3f)
	{
		printf("%d %d\n",dis[n],DP(n));
	}
	else
	{
		puts("No answer");
	}
	return 0;
}

【Luogu1608】路徑統計(最短路)(DP)