商品中心 | ||||||
| ||||||
Description | ||||||
有N个城镇(编号从1到N)城镇之间有一些双向道路相连,并且保证任意两个城镇之间只有一条唯一的路线。 每一条道路都有一个运送商品的容量。 假设城镇i到城镇j有一条路线,我们定义城镇i向城镇j运送商品的容量等于路线中道路运送商品的容量的最小值。 现在想建一个商品中心,并且要求商品中心到其他所有的城镇运送商品容量和最大。 | ||||||
Input | ||||||
有多组测试数据 第一行是一个整数N(1<=N<=200000) 接下来N-1行每行三个整数a,b,c表明城镇a和城镇b之间道路运送商品容量为c。 | ||||||
Output | ||||||
对于每组测试数据,输出一个整数商品中心到其他所有的城镇运送商品容量和最大值。 | ||||||
Sample Input | ||||||
4 1 2 2 2 4 1 2 3 1 4 1 2 1 2 4 1 2 3 1 | ||||||
Sample Output | ||||||
4 3 |
思路:
现在有一个最小权值作为约束,那么我们不妨先将所有边按照从大到小排序。
那么当前边的权值w,就是联通两个联通块的必经最小权值边。
那么对于当前这条边连接的两个联通块X.Y.要么我们让X中的一个点作为根,跑到Y中,使得这条边的贡献度为w*Y集合中点的个数,或者我们让Y中的一个点作为根,跑到X中,使得这条边的贡献度为w*X集合中点的个数。
无论我们怎样选,最终都会使得两个集合合并在一起。
那么我们肯定贪心去做,对于集合X.其中肯定有一个点作为根,已经跑遍了X中的其他所有点,其总权值和设定为ans【X】.对于结婚Y.也是肯定有一个点作为跟,已经跑遍了Y中的所有点,其总权值和设定为ans【Y】.根据贪心道理来讲,此时的X是局部贪心,Y也是局部贪心,那么两个局部贪心比较大小是可以得到整体贪心的。
那么我们连接两个点合并为同一连通块的时候,比较ans【X】+w*Y集合中点的个数和ans【Y】+w*X集合中点的个数的大小,让大的那边的根作为合并之后的集合的总根,达到当前局部最优贪心,从而不断的连接两个联通块,来得到整体的最优贪心。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int fa[maxn], sum[maxn], ans[maxn];
struct Edge
{
int u, v, w;
bool operator < (const Edge &other) const
{
return w > other.w;
}
}edge[maxn];
int getf(int x)
{
int ret = x, tmp;
while(ret != fa[ret])
ret = fa[ret];
while(fa[x] != x)
{
tmp = fa[x];
fa[x] = ret;
x = tmp;
}
return ret;
}
int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
for(int i = 1; i <= n; i++)
fa[i] = i, sum[i] = 1, ans[i] = 0;
for(int i = 0; i < n - 1; i++)
scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
sort(edge, edge+n-1);
for(int i = 0; i < n-1; i++)
{
int u, v, f1, f2, sum1, sum2;
u = edge[i].u;
v = edge[i].v;
f1 = getf(u);
f2 = getf(v);
sum1 = sum[f1];
sum2 = sum[f2];
if(ans[f2] + sum1*edge[i].w > ans[f1] + sum2*edge[i].w)
{
fa[f1] = f2;
sum[f2] += sum[f1];
ans[f2] += sum1 * edge[i].w;
}
else
{
fa[f2] = f1;
sum[f1] += sum[f2];
ans[f1] += sum2 * edge[i].w;
}
}
int output = 0;
for(int i = 1; i <= n; i++)
output = max(output, ans[i]);
printf("%d\n", output);
}
return 0;
}