题意描述:现有N-1个点,每两个点间一条道路,且任意两点之间只有一条路径,每条道路有一个货物容量,两点之间的最大货物容量是其路径中道路的货物容量最小值。先要选 择一点,使得它到其他N-1个点的货物容量总和最小。
分析 :
T-T 还是没能全靠自己做出来。
想到了按照边的权值从大到小排序,但是并不知道怎么合并,也无法证明贪心的正确性。
看了题解之后也想了好半天才想明白了贪心的原因(为什么他们的题解都各种想当然T-T)。每一次合并实际上都是在选择以左端所在集合的根节点为基点还是选择以右端所在集合的根节点为基点,当最后一步将两个集合合并后,得到最终答案。
如果选择左端,则最终的结果为 左端原权值+右边端点数*这条边的容量。
如果选择右端,则最终的结果为 右边原权值+左边端点数*这条边的容量。
比较可知如何合并。
然后还因为long long WA了一次。。真是醉。。
Memroy : 7892 Time: 1372
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200000+10;
struct Edge{
int x,y;
long long value;
}edge[maxn];
bool cmp(Edge a,Edge b){
return a.value>b.value;
}
int n;
int pa[maxn],cnt[maxn];
long long v[maxn];
void init(){
for(int i=0;i<n-1;i++){edge[i].x=0;edge[i].y=0;edge[i].value=0;}
for(int i=1;i<=n;i++){ pa[i]=i;v[i]=0;cnt[i]=1;}
}
int findset(int x){
if(pa[x]==x)return x;
else return pa[x]=findset(pa[x]);
}
int main(){
while(scanf("%d",&n)==1){
init();
for(int i=0;i<n-1;i++){
scanf("%d%d%lld",&edge[i].x,&edge[i].y,&edge[i].value);
}
sort(edge,edge+n-1,cmp);
for(int i=0;i<n-1;i++){
int x=findset(edge[i].x);
int y=findset(edge[i].y);
if(v[x]+edge[i].value*cnt[y]>v[y]+edge[i].value*cnt[x]){
pa[y]=x;
v[x]=v[x]+edge[i].value*cnt[y];
cnt[x]=cnt[x]+cnt[y];
}
else{
pa[x]=y;
v[y]=v[y]+edge[i].value*cnt[x];
cnt[y]=cnt[y]+cnt[x];
}
}
printf("%lld\n",v[findset(1)]);
}
return 0;
}