【WOJ 2244】统计最短路

本文探讨了一个特定的最短路径计数问题,其中包含如何判断是否存在无穷多种走法的策略。通过正向和反向进行最短路径算法,文章详细解释了如何在图中找到从起点到终点的所有可能路径数量,并特别关注了当存在边权为零的边时,如何确定路径解的无限性。
摘要由CSDN通过智能技术生成

【题目】

题目描述:

给出 n n n 个点, m m m 条带权无向边,问你从 1 1 1 号点到 n n n 号点的最短路中有多少种走法?

输入格式:

第一行两个数 n , m n,m n,m 分别表示点的个数和边的个数。( 2 2 2 n n n 5000 5000 5000 1 1 1 m m m 100000 100000 100000) 接下来 m m m 行,每行 3 3 3 个数 u , v , w u,v,w u,v,w 表示 u u u 号点到 v v v 号点有一条距离为 w w w 的边。( 1 1 1 u , v u,v u,v n n n 0 0 0 w w w 5000 5000 5000) 数据保证 1 1 1 号点能够到达 n n n 号点,点和边都可以被走多次。

输出格式:

如果有无穷种走法,输出 − 1 -1 1。否则输出走法的方案数 m o d mod mod 1000000009 1000000009 1000000009

样例数据:

输入
4 4
1 2 1
1 3 1
2 4 1
3 4 1

输出
2


【分析】

依旧是一个最短路计数模板题

不过这道题有一个新的东西:无穷种走法

那么怎样会是无穷种走法呢?由于没有负边权,所以只有当最短路中出现了边权为 0 0 0 边,就有无穷种走法

而现在的问题就是如何判断一条边是否在最短路之间出现过

其实也很简单,正着做一遍最短路( d d d),倒着做一遍最短路( d i s dis dis),如果 d i + d i s j + w i , j = d n d_i+dis_j+w_{i,j}=d_n di+disj+wi,j=dn,就出现过

然后其它的套模板就可以了


【代码】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5005
#define M 500005
#define Mod 1000000009
#define inf 1061109567
using namespace std;
int n,m,t,tot;
int d[N],num[N],dis[N];
int first[N],v[M],w[M],nxt[M];
priority_queue<pair<int,int> >q;
pair<int,int>edge[M];bool vis[N];
void add(int x,int y,int z)
{
	t++;
	nxt[t]=first[x];
	first[x]=t;
	v[t]=y;
	w[t]=z;
}
void dijkstra(int s)
{
	int x,y,i;
	memset(d,0x3f,sizeof(d));
	d[s]=0;q.push(make_pair(0,s));
	while(!q.empty())
	{
		x=q.top().second;q.pop();
		if(vis[x])continue;vis[x]=true;
		for(i=first[x];i;i=nxt[i])
		{
			y=v[i];
			if(d[y]>d[x]+w[i])
			{
				num[y]=num[x];
				d[y]=d[x]+w[i];
				q.push(make_pair(-d[y],y));
			}
			else  if(d[y]==d[x]+w[i])
			  num[y]=(num[y]+num[x])%Mod;
		}
	}
}
int main()
{
	int x,y,z,i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),add(y,x,z);
		if(!z)  edge[++tot]=make_pair(x,y);
	}
	dijkstra(n);
	memcpy(dis,d,sizeof(dis));
	memset(num,0,sizeof(num));
	memset(vis,false,sizeof(vis));
	num[1]=1,dijkstra(1);
	for(i=1;i<=tot;++i)
	{
		if(d[edge[i].first]+dis[edge[i].second]==d[n])
		{
			printf("-1");
			return 0;
		}
	}
	printf("%d",num[n]);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值