洛谷P1144 最短路计数 及其引申思考

图论题目练得比较少,发一道spfa的板子题目~

题目:P1144

题目描述

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

输入输出格式

输入格式:

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

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

输出格式:

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

输入输出样例

输入样例#1: 
5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5
输出样例#1:
1
1
1
2
4

说明

1到5的最短路有4条,分别为2条1-2-4-5和2条1-3-4-5(由于4-5的边有2条)。

对于20%的数据,N ≤ 100;

对于60%的数据,N ≤ 1000;

对于100%的数据,N<=1000000,M<=2000000。

Solution:

一眼数据范围,吓得就是一滚~,关键是还有重边,而且spfa只能记录单点到其它点的一条最短路,这题又要输出最短路个数取模,让人琢磨不透。So,默默的看看标签吧,普及+提高,嗯貌似不是很难,看下解题标签:spfa,bfs,图论,最短路。OK,果然还是要用到spfa,我们关键是要想到如何去处理重边和最短路计数。于是乎,我们先写一下spfa的模板,然后再来思考,不难发现:边权都为1,对于重边在spfa中会进行多次才到下一个点,所以路径数会记录下来,而且在spfa的松弛操作中由于边权均为1所以每个点只会松弛一次,于是乎在跑spfa时我们只需判断dis[v]==dis[u]+1是否成立,若成立,对于ans就加上到u点的边的条数再取模。这样这道题就完美的解决了。

代码:

 1 #include<bits/stdc++.h>
 2 #pragma GCC optimize(2)
 3 using namespace std;
 4 #define ll long long
 5 #define il inline
 6 #define mod 100003
 7 #define N 1000005
 8 #define inf 233333333
 9 int n,m,h[N],dis[N],cnt,ans[N];
10 bool vis[N];
11 queue<int>q;
12 il int gi()
13 {
14     int a=0;char x=getchar();bool f=0;
15     while((x<'0'||x>'9')&&x!='-')x=getchar();
16     if(x=='-')x=getchar(),f=1;
17     while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
18     return f?-a:a;
19 }
20 struct edge{
21 int to,net;
22 }e[N<<2];
23 il void add(int u,int v){e[++cnt].to=v;e[cnt].net=h[u];h[u]=cnt;}
24 il void spfa(int s)
25 {
26     for(int i=1;i<=n;i++)dis[i]=inf;
27     q.push(s);
28     vis[s]=1;ans[s]=1;dis[s]=0;
29     int u,v;
30     while(!q.empty())
31     {
32         u=q.front();
33         q.pop();vis[u]=0;
34         for(int i=h[u];i;i=e[i].net){
35             v=e[i].to;
36             if(dis[u]+1<dis[v]){
37                 dis[v]=dis[u]+1;
38                 ans[v]=ans[u];
39                 if(!vis[v])vis[v]=1,q.push(v);
40             }
41             else if(dis[v]==dis[u]+1)ans[v]=(ans[v]+ans[u])%mod;
42         }
43     }
44 }
45 int main()
46 {
47     n=gi(),m=gi();
48     int u,v;
49     for(int i=1;i<=m;i++)
50     {
51         u=gi(),v=gi();
52         add(u,v);add(v,u);
53     }
54     spfa(1);
55     for(int i=1;i<=n;i++)printf("%d
",ans[i]);
56     return 0;
57 }

思考:

由这道题我们想到,如果对与任意一个有向无环且带权的图,需要输出规定原点到其它点的最短路径条数,这样应该怎么去做呢?

我们可以先跑一遍spfa,再进行DAG+DP(或者记忆化搜索),也可以直接两遍spfa跑过(YZK大佬告诉我的方法,仔细想想思路都差不多,原理还是DP),于是乎,论DP的重要性,当然也可以看出spfa的应用有多广泛。。。

推广题:POJ3463(解题报告)

原文地址:https://www.cnblogs.com/five20/p/7782869.html