最短路计数

本文深入解析SPFA算法在解决图论中最短路径问题的应用,通过一道具体题目,详细阐述了如何使用SPFA算法计算从起点到图中各点的最短路径数量。文章包含完整的算法流程和代码实现,适合初学者理解并掌握SPFA算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

坑害我良久的一道比较水的spfa题

(传送门洛谷)

附上题目(测试数据自己看)

给出一个N个顶点M条边的无向无权图,顶点编号为1−N。问从顶点1开始,到其他每个点的最短路有几条。

这个题可以忽略重边(大不了自己走到自己再加上一条边)。以起点1号点出发,一遍spfa找出所有的dis值,开一个ans数组存答案。如找到更小的进行松弛,更新,并且它的ans这与它的前驱节点相同。再将v的前驱节点入队再找它前驱节点的最短路。
附上代码

if(dis[v]>dis[u]+1)
{
	dis[v]=dis[u]+1;
	ans[v]=ans[u];
	if(!pre[v])
	{
		q.push(v);
		pre[v]=1;
	}
}

用一个pre数组维护出我们要找到那个节点的前驱节点。如果dis[u]+1==dis[v],说明用最短路的走到u后再走到v都是1到v的一条合法最短路,所以v的答案=v的答案+u的答案。用加分原理累加起来就行了

下面是完整代码

#include<bits/stdc++.h>
#define mod 100003
using namespace std;
const int maxn=1e7;
int n,m,cnt=0;
int head[maxn],dis[maxn],ans[maxn],vis[maxn];
bool pre[maxn];
queue <int> q;
//priority_queue<int vector<int> greater<int> >q;

struct node{
	int v,nex;
}e[maxn<<1];

int read()
{
	int x=0;
	char ch=getchar();
	while(ch<'0' || ch>'9') ch=getchar();
	while(ch>='0' && ch<='9')
	{
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x;
}

void init()
{
	freopen("input.txt","r",stdin);
}

void add(int x,int y)
{
	e[++cnt].v=y;
	e[cnt].nex=head[x];
	head[x]=cnt;
}

void readdata()
{
	memset(dis,0x3f3f,sizeof(dis));//在memset中不要用1e7,我就在这儿调了40分钟
	memset(pre,0,sizeof(pre));
	memset(head,-1,sizeof(head));
	n=read();
	m=read();
	for(int i=1;i<=m;i++)
	{
		int a,b;
		a=read();
		b=read();
		add(a,b);
		add(b,a);
	}
}

void work()
{
	dis[1]=0;
	pre[1]=1;
	ans[1]=1;
	q.push(1);//将一号点赋予值
	while(!q.empty())//先跑一边spfa找到所有dis值
	{
		int u=q.front();
		pre[u]=0;
		q.pop();
		for(int i=head[u];~i;i=e[i].nex)
		{
			int v=e[i].v;
			if(dis[v]>dis[u]+1)
			{
				dis[v]=dis[u]+1;
				ans[v]=ans[u];//到v点的方案和到u点的方案一样
				if(!pre[v])
				{
					q.push(v);//将前驱点入队,找到它前驱点的最短路有多少条,到时候好累加。
					pre[v]=1;
				}
			}
			else if(dis[v]==dis[u]+1)//如果距离相等,累加起来
			{
				ans[v]+=ans[u]; 			   
				ans[v]%=mod;//不要忘了取模
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		printf("%d\n",ans[i]);
	}
}

int main()
{
	//init();
	readdata();
	work();
	return 0;
}

看来基础功夫还是任重而道远啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值