题解-[NOI2007]社交网络

题目链接

洛谷

题目描述:

我们可以通过统计经过一个结点 v v v 的最短路径的数目来衡量该结点在社交网络中的重要程度。考虑到两个结点 A A A B B B 之间可能会有多条最短路径。我们修改重要程度的定义如下:令 C s , t C_{s,t} Cs,t表示从s到t的不同的最短路的数目, C s , t , v C_{s,t,v} Cs,t,v表示经过 v v v s s s t t t 的最短路的数目;则定义 I ( v ) = ∑ s ≠ v , t ≠ v C s , t ( v ) C ( s , t ) I(v)=∑_{s≠v,t≠v} \frac{C_{s,t}(v)}{C_(s,t)} I(v)=s=v,t=vC(s,t)Cs,t(v) 为结点 v v v 在社交网络中的重要程度。现在给出这样一幅描述社交网络的加权无向图,请你求出每一个结点的重要程度。

题解:

这道题分两步来做,第一步求出每两个点的最短路,以及每个最短路的个数,第二步算出每一个 I ( v ) I(v) I(v)

由于n<=100,又因为要求多源最短路,所以显然可以用Floyd来解决

Floyd的算法核心就是以一个点为中介来更新另两个与其相邻的点的最短路,个人认为直接看代码更容易理解

在第一步中,我们设d[i][j]为从 ij 的最短路,然后枚举每一个除 ij以外的点k,如果d[i][k]+d[k][j]<d[i][j],即从i点经过k点到j点的最短路小于原来从i到j的最短路,那么就将新的最短路d[i][j]更新,然后将最短路的总数sum[i][j]也更新。因为在无向图中两个点互相最短路相等,所以d[j][i]=d[i][j];sum[j][i]=sum[i][j];而如d[i][k]+d[k][j]<d[i][j]即新的路径与原来的最短路相等,就加上它的数量。

sum[i][j]=sum[i][k]*sum[k][j],举个例子:a->b必须经过c,那么a->c有2条路,b->c有3条路,那么a->b就有2*3=6条路

for(int k=1;k<=n;k++)   //枚举每一个中间节点k
		for(int i=1;i<n;i++)   
			for(int j=i+1;j<=n;j++){       //枚举所有i,j
				if(i!=k&&j!=k)               //如果三个点互不相等
				if(d[i][k]+d[k][j]<d[i][j]){   //如果i-k的最短距离加上k-j的最短距离小于i-j的最短距离 
					d[i][j]=d[i][k]+d[k][j];  //把i-j的最短距离更新为 i-k的最短距离 + k-j的最短距离
					d[j][i]=d[i][j];
					sum[i][j]=sum[i][k]*sum[k][j];
					//把i-j的最短路数目更新为 i-k的最短路数目 + k-j的最短路数目
					sum[j][i]=sum[i][j];
				}
				else if(d[i][k]+d[k][j]==d[i][j]){//如果i-k的最短距离加上k-j的最短距离等于i-j的最短距离 
					sum[i][j]+=sum[i][k]*sum[k][j];//把i-j的最短路数目+= i-k的最短路数目 + k-j的最短路数目
					sum[j][i]=sum[i][j];
				}
			}

在第二步中,也是先枚举中间节点,因为所求答案就是根据每一个中间节点输出的
然后对于每一条需要经过它的最短路吧求和就行了
注意因为答案是小数,所以ans要用double赋值

for(int i=1;i<=n;i++){
		double ans=0;
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				if(j!=i&&k!=i&&d[i][j]+d[i][k]==d[k][j]){
//如果三个点互不相同且有最短路经过i点(j->k的最短距离= j->i的最短距离 + i->k的最短距离)
					ans+=(sum[i][j]*sum[i][k])/sum[j][k];
				}
			}
		}
		printf("%.3lf\n",ans);
	}

完整代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;

int d[101][101];
double sum[101][101];
int main(){
	int n,m;
	cin>>n>>m;
	int a,b,c;
	memset(d,0x3f3f3f3f,sizeof(d));
	for(int i=1;i<=m;i++){
		cin>>a>>b>>c;
		d[a][b]=c;
		d[b][a]=c;
		sum[a][b]=1;
		sum[b][a]=1;
	}
	for(int k=1;k<=n;k++)
		for(int i=1;i<n;i++)
			for(int j=i+1;j<=n;j++){
				if(i!=k&&j!=k)
				if(d[i][k]+d[k][j]<d[i][j]){
					d[i][j]=d[i][k]+d[k][j];
					d[j][i]=d[i][j];
					sum[i][j]=sum[i][k]*sum[k][j];
					sum[j][i]=sum[i][j];
				}
				else if(d[i][k]+d[k][j]==d[i][j]){
					sum[i][j]+=sum[i][k]*sum[k][j];
					sum[j][i]=sum[i][j];
				}
			}
	for(int i=1;i<=n;i++){
		double ans=0;
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				if(j!=i&&k!=i&&d[i][j]+d[i][k]==d[k][j]){
					ans+=(sum[i][j]*sum[i][k])/sum[j][k];
				}
			}
		}
		printf("%.3lf\n",ans);
	}
	return 0;
} 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值