noi2007 社交网络

社交网络 floy拓展

给定一个由n个点组成的无向图,求所有点的(V(x)=sum_{C(a,b)}^{C(a, b)(v)}(a e b)),其中(C(a, b))表示(a,b)这条最短路路径的条数,(C(a, b)(v))表示(a,b)最短路中经过v的最短路的条数。(nle 100)

这道题要分三个子问题:求出ab距离,求出ab最短路条数,求出ab最短路中,经过v的条数。如果第二个子问题求出来,用(path[a][b])表示ab间最短路的条数,v经过ab最短路的条数就是(dis[a][b]==dis[a][v]+dis[v][b]?path[a][v]*path[v][b]:0)。那么问题就是第二个问题如何解答。

如何套到floyd上求解第二个问题呢?首先想到预处理出所有点对距离,然后外层循环最短路中的点k,里层枚举i,j,看看是否(dis[i][k]+dis[k][j]==dis[i][j]),但是这样会把所有最短路上的点都算一遍,重复了。

所以我们有一个方法,就是把外层循环k定义为枚举ij之间的最大节点为k的最短路,因此第一个问题和第二个问题一起解决。这样就可以和floyd完美结合了。我们从理论角度理解一下,一个最短路,不会被k之前的外层循环枚举到,因为那个时候这个最短路还没有经过k,不是联通的,(dis[i][j]=infty),而更不会被k以后的外层循环枚举到,因为最短路中没有比k大的节点。世界真奇妙!

#include <cstdio>
using namespace std;

typedef long long LL;
const LL maxn=105, INF=1e9;
LL n, m, dis[maxn][maxn], path[maxn][maxn];

int main(){
    scanf("%lld%lld", &n, &m); LL x, y, z;
    for (LL i=1; i<=n; ++i)
        for (LL j=1; j<=n; ++j) dis[i][j]=INF;
    for (LL i=1; i<=m; ++i){
        scanf("%lld%lld%lld", &x, &y, &z);
        dis[x][y]=dis[y][x]=z;
        path[x][y]=path[y][x]=1;
    }
    for (LL k=1; k<=n; ++k){
        for (LL i=1; i<=n; ++i)
            for (LL j=1; j<=n; ++j){
                if (dis[i][k]+dis[k][j]>=INF) continue;
                if (dis[i][k]+dis[k][j]<dis[i][j]){
                    dis[i][j]=dis[i][k]+dis[k][j];
                    path[i][j]=path[i][k]*path[k][j];
                    continue; //注意这里
                }
                if (dis[i][k]+dis[k][j]==dis[i][j])
                    path[i][j]+=path[i][k]*path[k][j];
            }
    }
    double cnt=0;
    for (LL k=1; k<=n; ++k){
        cnt=0;
        for (LL i=1; i<=n; ++i)
            for (LL j=1; j<=n; ++j)
                if (i!=j&&dis[i][k]+dis[k][j]==dis[i][j])
                    cnt+=double(path[i][k])*path[k][j]/path[i][j];
        printf("%.3lf
", cnt);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/MyNameIsPc/p/7910562.html