【最短路径】【NOI2007】社交网路

问题描述
在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象。
不妨看这样的一个问题。在一个社交圈子里有 n 个人,人与人之间有不同程度的关系。我们将这个关系网络对应到一个 n 个结点的无向图上,两个不同的人若互相认识,则在他们对应的结点之间连接一条无向边,并附上一个正数权值 c,c 越小,表示两个人之间的关系越密切。
我们可以用对应结点之间的最短路长度来衡量两个人 s 和 t 之间的关系密切程度,注意到最短路径上的其他结点为 s 和 t 的联系提供了某种便利,即这些结
点对于 s 和 t 之间的联系有一定的重要程度。我们可以通过统计经过一个结点 v的最短路径的数目来衡量该结点在社交网络中的重要程度。
考虑到两个结点 A 和 B 之间可能会有多条最短路径。我们修改重要程度的定义如下:
令 Cs,t 表示从 s 到 t 的不同的最短路的数目, s,t(v)表示经过 v 从 s 到 t 的最短C路的数目;则定义

为结点 v 在社交网络中的重要程度。为了使 I(v)和 Cs,t(v)有意义,我们规定需要处理的社交网络都是连通的无向图,即任意两个结点之间都有一条有限长度的最短路径。
现在给出这样一幅描述社交网络的加权无向图,请你求出每一个结点的重要程度。
输入文件
输入文件中第一行有两个整数,n 和 m,表示社交网络中结点和无向边的数目。在无向图中,我们将所有结点从 1 到 n 进行编号。
接下来 m 行,每行用三个整数 a, b, c 描述一条连接结点 a 和 b,权值为 c 的无向边。注意任意两个结点之间最多有一条无向边相连,无向图中也不会出现自环(即不存在一条无向边的两个端点是相同的结点)。
输出文件
输出文件包括 n 行,每行一个实数,精确到小数点后 3 位。第 i 行的实数表示结点 i 在社交网络中的重要程度。
输入样例
44
121
231
341
411
输出样例
1.000
1.000
1.000
1.000
样例说明
社交网络如下图所示。

对于 1 号结点而言,只有 2 号到 4 号结点和 4 号到 2 号结点的最短路经过 1号结点,而 2 号结点和 4 号结点之间的最短路又有 2 条。因而根据定义,1 号结1 1点的重要程度计算为 + = 1 。由于图的对称性,其他三个结点的重要程度也都2 2是 1。
评分方法
本题没有部分分,仅当你的程序计算得出的各个结点的重要程度与标准输出相差不超过0.001时,才能得到测试点的满分,否则不得分。
数据规模和约定
50%的数据中:n ≤ 10,m ≤ 45
100%的数据中:n ≤ 100,m ≤ 4 500,任意一条边的权值 c 是正整数,满足:1 ≤ c ≤ 1 000。
所有数据中保证给出的无向图连通,且任意两个结点之间的最短路径数目不超过 1010。

一道简单的Floyd最短路问题,但考试时只得了50分,原因在于计算方案的时候重复计算了。
比如A到D有一条最短路径为A <-> B <-> C <-> D,那么照原来的计算方程(见图formula.png),就会将A到D的最短路经条数算成2。
正解是一条路经上的结点只累加一次,即在Floyd的过程中顺便维护。
代码:

/***************************\
 * @prob: NOI2007 network  *
 * @auth: Wang Junji       *
 * @stat: Accepted.        *
 * @date: May. 24th, 2012  *
 * @memo: Floyed           *
\***************************/
#include <cstdio>

const int maxN = 110, INF = 0x3f3f3f3f;
int f[maxN][maxN], n, m; long long g[maxN][maxN];
//g数组要用long long存储,否则不够。

int main()
{
    freopen("network.in", "r", stdin);
    freopen("network.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < n + 1; ++j)
        g[i][j] = 1, f[i][j] = INF;
    while (m--)
    {
        int u, v, d; scanf("%d%d%d", &u, &v, &d);
        f[u][v] = f[v][u] = d;
    }
    for (int k = 1; k < n + 1; ++k)
    for (int i = 1; i < n + 1; ++i) if (k - i)
    for (int j = 1; j < n + 1; ++j) if (i - j && k - j)
    {
        if (f[i][k] + f[k][j] < f[i][j])
            f[i][j] = f[i][k] + f[k][j],
            g[i][j] = g[i][k] * g[k][j];
        //如果i到j的最短路径被松弛,则更新g,重新累加。
        else if (f[i][k] + f[k][j] == f[i][j])
            g[i][j] += g[i][k] * g[k][j];
        //如果k是i到j最短路径上的一点,则累加最短路径条数。
    }
    for (int k = 1; k < n + 1; ++k)
    {
        double ans = 0;
        for (int i = 1; i < n + 1; ++i) if (i - k)
        for (int j = 1; j < n + 1; ++j)
        if (i - j && k - j && f[i][k] + f[k][j] == f[i][j])
            ans += (double)g[i][k] * g[k][j] / g[i][j];
        printf("%.3lf\n", ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值