P1608 最短路计数的另一种解决方式

路径统计

题目描述

“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 \ne J\)\(1 \leq I, J\leq N\))。

输出格式

输出文件包含两个数,分别是最少花费和花费最少的路径的总数。保证花费最少的路径的总数不超过 \(2^{30}\)

两个不同的最短路方案要求:路径长度相同(均为最短路长度)且最短路经过的点的编号序列不同。

若城市 \(N\) 无法到达则只输出一个 No answer

样例 #1

样例输入 #1

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

样例输出 #1

4 2

提示

对于 \(30\%\) 的数据 \(N\leq 20\)

对于 \(100\%\) 的数据 \(1\leq N\leq 2000\)\(0\leq E\leq N\times (N-1)\)\(1\leq C\leq 10\)

dijkstra+堆优化 虽说WA了一个点 但问题不大

这种方法是边求最短路边递推更新 num[ ]

u--->v 就 num[v]+=num[u]

若更新了 就覆盖 num[v]=num[u]

#include<bits/stdc++.h>// 有向图    最短路径计数 
using namespace std;
#define int long long
const int N=2005;
const int M=4e6+5;
const int inf=1e16;
int G[N][N];
// 1 ----> n
struct Graph {
	int nxt,to,val;
} edge[M<<1];
int head[M],cnt;
int n,m;
inline void add(int u,int v,int w) {
	cnt++;
	edge[cnt].to=v;
	edge[cnt].val=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
struct did {
	int u,d;
	bool operator < (const did& t)const {
		return t.d<d;
	}
};
int dis[N],num[N],dis2[N];
void dijkstra(int s) {
	priority_queue<did>q;
	for(int i=1; i<=n; i++)dis[i]=inf;
	dis[s]=0,num[s]=1;
	q.push({s,dis[s]});
	while(!q.empty()) {
		did tmp=q.top();
		q.pop();
		int d=tmp.d,u=tmp.u;
		if(dis[u]!=d)continue;
		for(int i=head[u]; i; i=edge[i].nxt) {
			int v=edge[i].to;
			int w=edge[i].val;
			if(dis[u]+w==dis[v])num[v]+=num[u];
			else if(dis[u]+w<dis[v]) {
				dis[v]=dis[u]+w;
				num[v]=num[u];
				q.push({v,dis[v]});
			}
		}
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1; i<=m; i++) {
		int u,v,w;
		cin>>u>>v>>w;
		if(G[u][v])continue;
		G[u][v]=1;
		add(u,v,w);
	}
	dijkstra(1);
	if(dis[n]==inf)cout<<"No answer\n";
	else cout<<dis[n]<<" "<<num[n]<<"\n";
	return 0;
}

UPD:
但是这种解决重边情况只能适用于n不大的情况 大了的话是没办法开G来判重的
所以最好的做法还是两次dijkstra
第一次算出最短路的值 第二次直接if(dis[u]+w==dis[v]) num[v]+=num[u]即可

posted @ 2023-04-13 20:58  N0zoM1z0  阅读(2)  评论(0编辑  收藏  举报