版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/82969893
题目链接:hdu 5723
参考博客:https://www.cnblogs.com/aiguona/p/7214707.html
题意:给出n个顶点,m条边,让你建一颗最小生成树,问:最小生成树的值是多少,任意两点的期望长度是多少?
题解:
任意两点的期望是(权值)*(这条路被走过的次数)的总和 除以 总共的路径数。
比如从A到B距离为2,从B到C距离为3。那么从A到B走过2一次,从A到C走过2一次,走过3一次,从B到C走过3一次。
所以期望为(2*2+3*2)/ 3 = 3.33。(也可以看成有三条路径,1,A->B , B->C , A->C 其实B->A也算一种的,但我们可以不用考虑,因为求得是期望值,所以我们就按顺着的方向来算路径)。
总共的路径数为 n*(n-1)/2 ,举个例子给你,有4个顶点,那么就有3个值,那么首先有3条路径,分别为 A->B B->C C->D (经过两个顶点),
然后有2条路径,分别为 A->C B->D(经过三个顶点) , 最后有1条路径,为 A->D(经过4个顶点),很容易可以看出是个等差数列和(每次少一条路径)。
这里不能求最短路,超时。
应该用深搜回溯,找出这条路被走过过少次。这样就可以求出期望了。
这里由于数据量比较大,不能用prim邻接矩阵求最小生成树(vector应该可以),可以用Kruskal求最小生成树。
坑点:注意最后求期望的时候总共的路径数量n*(n-1)/2数据量比较大,应该用long long或者double存储。
///代码参考博客:https://www.cnblogs.com/aiguona/p/7214707.html
#include<cstdio>
#include<algorithm>
#include<vector>
#include<iostream>
#include<string.h>
using namespace std;
vector<pair<int,int> > v[100010];
struct Edge
{
int f,t,q;
};
int m,n;///n为村庄数,m为街道数
Edge s[1000010];///存储图
long long ans;///存最后的每条路的总和
int pre[100010];///并查集的祖先数组
int vis[100010];///标记数组
bool cmp(Edge a,Edge b )///排序函数
{
return a.q<b.q;
}
int Find(int x)///找祖先
{
if(x!=pre[x])
{
pre[x]=Find(pre[x]);
}
return pre[x];
}
void Merge(int x,int y)///查是否相等
{
int fx=Find(x);
int fy=Find(y);
if(fx!=fy)
pre[fx]=fy;
}
long long dfs(int x) ///dfs递归搜索
{
vis[x]=1;
long long now=0,all=1;///now记录当前节点直接连接的节点数量 all记录此节点经过搜索后所有的与此节点连接的节点数
int h=v[x].size();
for(int i=0; i<h; i++)
{
int b=v[x][i].first;
if(!vis[b])
{
now=dfs(b);
all+=now;
// printf("b=%d,all=%lld,now=%lld\n",b,all,now);
///路径数为 now*(n-now),解释下:与x节点连接的后续节点数为now个,那么与x连接的前置节点就有(n-now)个,
///所有的前置节点都可以和后续节点形成一条顺着的路径
///故为 now*(n-now)条路径
ans+=now*(n-now)*v[x][i].second;///ans记录的是权值*经过的次数
}
}
return all;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ans=0;
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&m);
if(m==0||n==0)
{printf("0 0.00");continue;}
for(int i=0;i<=n;i++)
v[i].clear();
for(int i=0; i<=n; i++)///并查集的祖先节点的初始化
{
pre[i]=i;
}
for(int j=0; j<m; j++)///输入
{
scanf("%d%d%d",&s[j].f,&s[j].t,&s[j].q);
}
sort(s,s+m,cmp);///排序
long long sum=0;///sum用来记录最小生成树的长度
for(int j=0; j<m; j++)
{
int fx=Find(s[j].f);
int fy=Find(s[j].t);
if(fx!=fy) ///如果祖先不相等,那么加入到最小生成树中
{
sum=sum+s[j].q;
Merge(fx,fy);
///加入到动态数组中准备做期望
v[s[j].f].push_back(make_pair(s[j].t,s[j].q));
v[s[j].t].push_back(make_pair(s[j].f,s[j]. q));
}
}
dfs(1);///深搜回溯计算ans的值
double y=1.0*n*(n-1)/2;
printf("%lld %.2lf\n",sum,(double)ans/y);
}
return 0;
}