P2047 [NOI2007] 社交网络 题解
_Ayanami_Rei_ · · 题解
这是本蒟蒻第一次发题解,还有很多不足之处,望请教!
题目传送门:P2047 [NOI2007] 社交网络
题意分析
这道题看似十分的绕,实则十分的简单:对于点 感觉用文字表述比公式更难看懂。
解题思路
首先大家看到题面,就都可以感受到这是一个多源的最短路问题,加上数据范围里的
初始化
在最开始全部初始化为
代码如下:
memset (dis, 0x3f, sizeof dis);
memset (sum, 0, sizeof sum);
scanf ("%d %d", &n, &m);
for (int i = 1;i <= n; ++i) {
dis[i][i] = 0;
}
for (int i = 1;i <= m; ++i) {
int a, b, c;
scanf ("%lld %lld %lld", &a, &b, &c);
dis[a][b] = dis[b][a] = c;
++sum[a][b][0], ++sum[b][a][0];
}
更新
不难看出,当有一个点
1.dis_{i,k}+dis_{k,j}<dis_{i,j} 时
更新
然而,因重要程度不算起点终点,所以当
同样的,对于在
更新完路径上的点的重要值,便要更新最短路的条数了,同样根据乘法原理易得:
代码如下:
if (dis[i][j] > dis[i][k] + dis[k][j]) {
dis[i][j] = dis[i][k] + dis[k][j];
fill (sum[i][j], sum[i][j] + 1 + n, 0);
for (int u = 1;u <= n; ++u) {
if (i == u || u == j) continue;
sum[i][j][u] += sum[i][k][u] * sum[k][j][0];
sum[i][j][u] += sum[k][j][u] * sum[i][k][0];
}
sum[i][j][k] += sum[i][k][0] * sum[k][j][0];
sum[i][j][0] += sum[i][k][0] * sum[k][j][0];
}
2.dis_{i,k} + dis_{k,j} = dis_{i,j} 时
对于这一种情况,与上一种处理方法是很相似的,只是不用清空
if (dis[i][j] == dis[i][k] + dis[k][j]) {
for (int u = 1;u <= n; ++u) {
if (i == u || u == j) continue;
sum[i][j][u] += sum[i][k][u] * sum[k][j][0];
if (u != k) {
sum[i][j][u] += sum[k][j][u] * sum[i][k][0];
}
}
sum[i][j][k] += sum[i][k][0] * sum[k][j][0];
sum[i][j][0] += sum[i][k][0] * sum[k][j][0];
}
输出
最后这一段没有什么好讲了的,根据题意就可以轻而易举地得出下面这条公式:
代码实现如下:
for (int k = 1; k <= n; ++k) {
double ans = 0.0;
for (int i = 1; i <= n; ++i) {
if (i == k) continue;
for (int j = 1; j <= n; ++j) {
if (j == k) continue;
if (sum[i][j][0] != 0)
ans += sum[i][j][k] * 1.0 / sum[i][j][0];
}
}
printf ("%.3f\n", ans);
}
AC代码
将上述代码加以整合简化,便可以得出下面的AC代码:
#include <iostream>
#include <cstring>
using namespace std;
#define int long long
// 两点间最短路数目不超过1e10,记得开long long
int dis[101][101], sum[101][101][101], n, m;
signed main () {
memset (dis, 0x3f, sizeof dis);
const int INF = dis[0][0];
scanf ("%d %d", &n, &m);
for (int i = 1;i <= n; ++i) {
dis[i][i] = 0;
}
for (int i = 1;i <= m; ++i) {
int a, b, c;
scanf ("%lld %lld %lld", &a, &b, &c);
dis[a][b] = dis[b][a] = c;
++sum[a][b][0], ++sum[b][a][0];
}
for (int k = 1;k <= n; ++k) {
for (int i = 1;i <= n; ++i) {
for (int j = 1;j <= n; ++j) {
if (i == j || j == k || i == k) continue;
if (dis[i][j] > dis[i][k] + dis[k][j]) {
dis[i][j] = dis[i][k] + dis[k][j];
// 这里先不做更改,对dis赋值以进入下面的if来更新
fill (sum[i][j], sum[i][j] + 1 + n, 0);
}
if (dis[i][j] == dis[i][k] + dis[k][j]) {
for (int u = 1;u <= n; ++u) {
if (i == u || u == j) continue;
sum[i][j][u] += sum[i][k][u] * sum[k][j][0];
sum[i][j][u] += sum[k][j][u] * sum[i][k][0];
}
sum[i][j][k] += sum[i][k][0] * sum[k][j][0];
sum[i][j][0] += sum[i][k][0] * sum[k][j][0];
}
}
}
}
for (int k = 1; k <= n; ++k) {
double ans = 0.0;
for (int i = 1; i <= n; ++i) {
if (i == k) continue;
for (int j = 1; j <= n; ++j) {
if (j == k) continue;
if (sum[i][j][0] != 0)
// 除数不能为0
ans += sum[i][j][k] * 1.0 / sum[i][j][0];
}
}
printf ("%.3f\n", ans);
}
return 0;
}
鸣谢
在这片题解的最后,我想感谢一下同一机房的两位大佬 _Kaedehara_Kazusha_ 和 Tzy090420,给了本蒟蒻众多帮助来A这道题(没错,我就菜到这题都能卡我一下)!!!