AcWing1134最短路计数(spfa)

题目地址https://www.acwing.com/problem/content/1136/

题目描述

给出一个 N 个顶点 MM 条边的无向无权图,顶点编号为 11 到 NN。

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

输入格式

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

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

输出格式

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

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

数据范围

1N1e5
1M2e5

题解:最短路问题最常见的是求最短路的权值,这道题让求的是最短路的条数。对于这类问题,也有很多的求解方法。

我们可以求出最短路的一个拓扑图,首先利用dijsktra求出所有点,不过这里求的是,如果结点j的最短路是结点i,那么就存储下i是j的前驱,一个结点可能会有多条前驱,需要全部记下来,那么这样存储的就是一个新的无环的拓扑图,然后直接从源点s进行bfs就可以了,对于从队列中出来的结点,出来几次,那么到该结点的最短路就有几条。

当然,上面的这个方法,首先需要保证不存在负圈,并且也不能存在权值为0的环。

另一个办法就是利用spfa的松弛操作。如果队列当前拿出的结点为now,那么遍历now的每条边,如果边的另一个结点为v;dis[v]<=dis[now],那么结点v不能更新v,就忽略;dis[v]==dis[now]+1,那么这条路也是当前(注意这里不一定是全局)最短路,让num[v]+=num[now](num[i]表示当前到结点i的最短路的条数);dis[v]>dis[now]+1,那么结点v的最短路就会被更新,让num[v]=num[now]即可。

AC代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e5+10,M=2e5+10,mod=1e5+3;
typedef pair<int,int> PII;
struct node{
    int from,to,next;
}edge[M*2];
int head[N],cnt,dis[N],num[N];
int n,m;

void addedge(int u,int v){
    cnt++;
    edge[cnt].from=u;
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}

void spfa(int s){
    int st[N]={0};
    dis[1]=0;
    num[s]=1;
    queue<int>q;
    q.push(s);
    st[s]=1;
    while(!q.empty()){
        int now=q.front();q.pop();
        st[now]=0;
        for(int i=head[now];~i;i=edge[i].next){
            if(dis[edge[i].to]<=dis[now]) continue;
            if(dis[edge[i].to]==dis[now]+1) num[edge[i].to]+=num[now];
            else num[edge[i].to]=num[now];
            num[edge[i].to]%=mod;
            dis[edge[i].to]=dis[now]+1;
            if(st[edge[i].to]) continue;
            q.push(edge[i].to);
            st[edge[i].to]=1;
        }
    }
}

int main(){
    memset(head,-1,sizeof(head));
    memset(num,0,sizeof(num));
    memset(dis,0x3f,sizeof(dis));
    cin>>n>>m;
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
    }
    spfa(1);
    for(int i=1;i<=n;i++) cout<<num[i]<<endl;
    return 0;
}

写于:2020/9/10 15:52

posted @ 2020-09-10 15:53  白菜茄子  阅读(166)  评论(0编辑  收藏  举报