每周一算法:最短路计数

ops/2024/10/18 12:24:28/

题目描述

给出一个 N N N个顶点 M M M 条边的无向无权图,顶点编号为 1 1 1 N N N

问从顶点 1 1 1 开始,到其他每个点的最短路有几条。

输入格式

第一行包含 2 2 2 个正整数 N , M N,M N,M,为图的顶点数与边数。

接下来 M M M 行,每行两个正整数 x , y x,y x,y,表示有一条顶点 x x x 连向顶点 y y y 的边,请注意可能有自环与重边。

输出格式

输出 N N N 行,每行一个非负整数,第 i i i 行输出从顶点 1 1 1 到顶点 i i i 有多少条不同的最短路,由于答案有可能会很大,你只需要输出对 100003 100003 100003 取模后的结果即可。

如果无法到达顶点 i i i 则输出 0 0 0

样例 #1

样例输入 #1

5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5

样例输出 #1

1
1
1
2
4

提示

【数据范围】
1 ≤ N ≤ 1 0 5 1≤N≤10^5 1N105,
1 ≤ M ≤ 2 × 1 0 5 1≤M≤2×10^5 1M2×105

算法思想

根据题目描述,求从起点 1 1 1 到每个顶点有多少条不同的最短路,即求最短路的方案数。可以使用动态规划的思想,定义状态 f [ i ] f[i] f[i]表示起点到顶点 i i i的最短路的方案数。

要在图中计算状态,需要先构造出图的拓扑序列。但是题目中给出的是无向无权图,通过测试样例可以看出,图中有可能存在环,如下图所示,因此不一定存在拓扑序列,因此不能通过循环迭代直接计算状态。
在这里插入图片描述
由于求的是最短路的方案数,考虑能否使用最短路算法,在计算最短路的同时将状态计算出来。要使用最短路计算方案数需要要满足不能存在代价为 0 0 0的环,否则经过该环的最短路的方案数为无穷大。而题目中题目给出的是无向无权图,可以认为边权都为 1 1 1,因此不存在代价为 0 0 0的环。

那么有哪些最短路算法可以进行状态计算呢?

题目求的是起点到其余顶点的最短路,那么可以选择的最短路算法有:BFS、Dijkstra、Bellman-Ford(SPFA)等,是不是这些算法都可以用来计算状态呢?要计算 i i i点的状态,必须保证 i i i阶段所依赖的状态都已经被计算出来,也就是说在计算最短路时,能够找到一个拓扑序来计算状态。这样就需要最短路算法能够构造出一个最短路拓扑图。如下图所示:
在这里插入图片描述

而对于下列算法

  • BFS算法计算(边权相同的)最短路时,是一层一层进行扩展的,每个点只会入队 1 1 1次,出队 1 1 1次,进队和出队都是满足拓扑序的。
  • Dijkstra算法计算最短路时,每个点只会出队 1 1 1次,当一个点出队时,是不会更新前面已经出队的点到起点的最短路,因此出队序列也满足拓扑序。
  • Bellman-Ford(SPFA)算法计算最短路时,每个点可以入队出队多次,当一个点出队时可能会更新之前出队的点的最短路,这样就不能保证出队序列具备拓扑序。

也就是说, BFS和 Dijkstra算法在求最短路过程中可以进行状态计算。

算法流程

  • 将起点 1 1 1的最短路径方案数初始化为 1 1 1,即 f [ 1 ] = 1 f[1] = 1 f[1]=1
  • 在求解最短路的过程中
    • v v v点到起点的最短路能够被 u u u点松弛,即 d i s [ v ] > d i s [ u ] + 1 dis[v] > dis[u] + 1 dis[v]>dis[u]+1,则到 v v v的最短路方案数等于到 u u u点的最短路方案数,即 f [ v ] = f [ u ] f[v] = f[u] f[v]=f[u]
    • v v v点到起点的最短路等于经过 u u u点中转的距离,则需要累加到 u u u点的最短路方案数,即 f [ v ] = f [ v ] + f [ u ] f[v] =f[v] + f[u] f[v]=f[v]+f[u]

代码实现

#include <bits/stdc++.h>
//注意:边数为200000,无向图需要建双向边,所以边的总数为400010
const int N = 100010, M = 400010, mod = 100003;
int n, m;
int h[N], e[M], ne[M], idx;
//dist[i]表示从1号点到i号点的最短距离
//f[i]表示从1到点到i号点的最短距离的路径数
int dis[N], f[N], q[N];
void add(int a, int b)
{e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void bfs()
{memset(dis, 0x3f, sizeof dis);//注意,到1号点的最短路径数初始为1dis[1] = 0, f[1] = 1;//bfs每个点只会入队一次,使用普通队列即可int hh = 0, tt = 0;q[0] = 1;while(hh <= tt){int u = q[hh ++];for(int i = h[u]; ~i; i = ne[i]){int v = e[i];//可以更新最短距离if(dis[v] > dis[u] + 1){dis[v] = dis[u] + 1;q[++ tt] = v;//此时的路径数不变f[v] = f[u];}   else if(dis[v] == dis[u] + 1)//如果最短距离相等,累加最短路径数{f[v] = (f[v] + f[u]) % mod;}}}
}int main()
{scanf("%d%d", &n, &m);memset(h, -1, sizeof h);while(m --){int a, b;scanf("%d%d", &a, &b);add(a, b), add(b, a);  //无向图,双向边}bfs();for(int i = 1; i <= n; i++) printf("%d\n", f[i]);return 0;
}

http://www.ppmy.cn/ops/15611.html

相关文章

mybatis-3.5.0使用插件拦截sql以及通用字段赋值

1、添加插件配置类 import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; imp…

docker教程(详细)

0 背景 软件开发最大的麻烦事之一&#xff0c;就是环境配置。环境配置如此麻烦&#xff0c;换一台机器&#xff0c;就要重来一次&#xff0c;旷日费时。很多人想到&#xff0c;能不能从根本上解决问题&#xff0c;软件可以带环境安装&#xff1f;也就是说&#xff0c;安装的时…

Leetcode 150:逆波兰表达式求值

给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 示例 1&#xff1a; 输入&#xff1a;tokens ["2","1","","3","*"] 输出&…

桌面运维类面试非技术问题

1、计算机系统卡顿怎么处理 计算机系统卡顿可以通过硬件升级、系统优化、软件优化和安全防护等措施可解决。 包括增加内存条、更换固态硬盘、磁盘清理、关闭后台进程、禁用开机启动项、重装系统和更新驱动程序等。 卸载不常用软件、使用轻量级软件和开启防火墙也是有效的方法。…

浅谈操作系统中的重要概念——进程

文章目录 一、什么是程序&#xff1f;二、什么是进程&#xff1f;三、进程与程序有什么区别&#xff1f;四、OS是如何管理进程的4.1、使用 结构体 进行描述进程4.2 、使用数据结构组织众多进程4.3、PCB4.3.1、PCB 里有哪些属性4.3.1.1 pid4.3.1.2 内存指针4.3.1.3 文件描述符表…

shell--for循环

1.带列表for循环 语法格式 for 循环变量 in 列表 do执行语句... done 在上面的语法中&#xff0c;循环变量是每次循环时得到的列表的某一个数据&#xff0c;当循环一次结束后&#xff0c;再获取另一个数&#xff0c;然后再执行 do 里面的语句&#xff0c;依次类推&#xff0…

golang面试题:拷贝大切片一定比小切片代价大吗?

问题 拷贝大切片一定比小切片代价大吗&#xff1f; 怎么答 并不是&#xff0c;所有切片的大小相同&#xff1b;三个字段&#xff08;一个 uintptr&#xff0c;两个int&#xff09;。切片中的第一个字是指向切片底层数组的指针&#xff0c;这是切片的存储空间&#xff0c;第二…

SystemC 等待异步事件解决方案

本文为实现 SystemC 响应异步事件 解决方案。 应用场景&#xff1a; SystemC是一个支持系统事务级、行为级建模的开源的C library&#xff1b; 我们将SystemC仿真的模拟叫做模拟器。在很多场景下&#xff0c;模拟器要保持alive&#xff0c;等待异步async事件&#xff0c;做出…