C-旅行
题目描述
DK 有一个无向图 G,这个无向图有 n 个点 m 条边
你需要确定一个大小为 n 的排列 a,使 最大,求这个最大值
表示从 u 到 v 的路径的中最短的边的边权,若有多条路径,则选令
最大的路径
输入描述:
第一行两个正整数 n,m
接下来 m 行,每一行三个正整数 u,v,w 表示 u,v 之间有一条长度为 w 的边
输出描述:
仅一行,表示最大的
昨晚做的时候竟然能把看成u和v之间的最长路径的长度;
思路:最大生成树;
其实不证明的话猜两组数据就能看出来;证明的话放官方证明吧:
作者:scimoon
可以发现,对答案有贡献的边肯定是最大生成树上的边,那么可以将这些边先拉出来,每条边至少会被贡献一次
对于当前的一个联通块,找到最小的一条边,那么这个联通块肯定被分成了两个联通块
考虑怎么样才能使答案最优,显然先将一个联通块内选完以后在经过当前边到另一个联通块最优(因为两边的边比当前边要大),可以看出这条边只对答案贡献了一次
这样分治下去就可以得到:将所有生成树上的边的权值加起来就是答案
(长时间没写kruskal算法,模板都要忘了)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+7;
int n, m;
int p[N];
struct Edge
{
int a, b, w;
bool operator< (const Edge &W)const
{
return w > W.w;
}
}edges[N];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
ll kruskal()
{
sort(edges, edges + m);
for (int i = 1; i <= n; i ++ ) p[i] = i;
ll res = 0;
for (int i = 0; i < m; i ++ )
{
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
// cout <<a<<" "<<b<<" "<<w<<endl;
a = find(a), b = find(b);
if (a != b)
{
p[a] = b;
res += w;
}
}
return res;
}
int main()
{
cin >>n>>m;
for(int i=0;i<m;i++) cin >>edges[i].a>>edges[i].b>>edges[i].w;
cout <<kruskal()<<endl;
}