NOI2007 社交网络

题目:社交网络

网址:https://www.luogu.com.cn/problem/P2047

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

输入格式

输入第一行有两个整数\(n\)\(m\),表示社交网络中结点和无向边的数目。
在无向图中,我们将所有结点从\(1\)\(n\)进行编号。

接下来\(m\)行,每行用三个整数\(a,b,c\)描述一条连接结点\(a\)\(b\),权值为\(c\)的无向边。 注意任意两个结点之间最多有一条无向边相连,无向图中也不会出现自环(即不存在一条无向边的两个端点是相同的结点)。

输出格式

输出包括\(n\)行,每行一个实数,精确到小数点后\(3\)位。第\(i\)行的实数表示结点\(i\)在社交网络中的重要程度。

输入输出样例

输入

4 4
1 2 1
2 3 1
3 4 1
4 1 1

输出

1.000
1.000
1.000
1.000

说明/提示

image
对于\(1\)号结点而言,只有\(2\)号到\(4\)号结点和\(4\)号到\(2\)号结点的最短路经过\(1\)号结点,而\(2\)号结点和\(4\)号结点之间的最短路又有\(2\)条。因而根据定义,\(1\)号结点的重要程度计算为\(1/2+1/2=1\)。由于图的对称性,其他三个结点的重要程度也都是\(1\)

对于\(50%\)的数据,\(n≤10,m≤45\)
对于\(100%\)的数据,\(n≤100,m≤4500\),任意一条边的权值\(c\)是正整数且\(1⩽c⩽1000\)
所有数据中保证给出的无向图连通,且任意两个结点之间的最短路径数目不超过\(10^{10}\)


很有意思的一道最短路问题(Floyd)。
不难想到使用\(cnt[i][j]\)记录结点\(i\)到结点\(j\)的最短路径的条数。
显然,\(cnt[i][i] = 0\)
DP方程不加赘述了。留意:在转移的时候,应当考虑乘法原理。
统计怎么办?显然暴力,遍历\(s\)\(t\)\(C_{s,t}(v)\)判断一下:\(d[i][j]\)是否等于\(d[i][k]+d[k][j]\),累加\(cnt[i][k]*cnt[j][k]\)即可。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 100 + 5;
int n, m, d[maxn][maxn];
long long cnt[maxn][maxn];
int main()
{
	memset(d, 0x3f, sizeof(d));
	memset(cnt, 0, sizeof(cnt));
	scanf("%d %d", &n, &m);
	int x, y, z; 
	for(int i = 1; i <= n; ++ i) d[i][i] = 0;
	
	for(int i = 1; i <= m; ++ i)
	{
		scanf("%d %d %d", &x, &y, &z);
		d[x][y] = d[y][x] = z;
		cnt[x][y] = cnt[y][x] = 1;
	}
	
	for(int k = 1; k <= n; ++ k)
	{
		for(int i = 1; i <= n; ++ i)
		{
			if(i == k) continue;
			for(int j = 1; j <= n; ++ j)
			{
				if(j == k) continue;
				if(d[i][j] == d[i][k] + d[k][j]) cnt[i][j] += cnt[i][k] * cnt[k][j];
				
				if(d[i][j] > d[i][k] + d[k][j])
				{
					d[i][j] = d[i][k] + d[k][j];
					cnt[i][j] = cnt[i][k] * cnt[k][j];
				}
			}
		}
	}
	for(int k = 1; k <= n; ++ k)
	{
		double t = 0.000;
		for(int i = 1; i <= n; ++ i)
		{
			if(i == k) continue;
			for(int j = 1; j <= n; ++ j)
			{
				if(j == k) continue;
				if(d[i][j] == d[i][k] + d[k][j])
					t += (double) (cnt[i][k] * cnt[k][j]) / cnt[i][j];
			}
		}
		printf("%.3lf\n", t);
	}
	return 0;
}
posted @ 2020-07-30 19:32  大秦帝国  阅读(129)  评论(0)    收藏  举报