题目给出了几个点,去掉生成树中的边,使这几个点相互孤立,求去掉的边的总长度之和最小。如果顺着题目的思路去想,可能有点难得想,此时可以尝试换一个思路:模拟建树的过程。首先随便取两个点,如果这两个点需要相互孤立,那么就不取它们之间的边,反之则取。用并查集来判断两个点是否需要相互孤立。最后用总的边的长度减去取的边的长度,就是答案了,在此之前将边从大到小排个序比较好,这样才能保证去掉的是最小长度嘛。我用的是优先队列,当然用数组排序是一样的。
/*************************************************************************
> File Name: main.cpp
> Author:Eagles
> Mail:None
> Created Time: 2018年09月05日 星期三 11时09分01秒
> Description:HDU4313,kruskal,并查集
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;//用int会wa
ll n,m;
#define N 100005
ll par[N];
ll dst[N];//用来保存需要相互孤立的点
ll sum;
struct node
{
ll a,b;
ll val;
bool operator <(node com) const
{
return val<com.val;
}
};
priority_queue<node>Q;
ll find_par(ll x)
{
return x==par[x]?x:par[x]=find_par(par[x]);
}
void unite(ll x, ll y)
{
ll fx=find_par(x);
ll fy=find_par(y);
par[fy]=fx;
}
void init()
{
for (ll i=0; i<=n; i++)
par[i]=i;
sum=0;//总的长度
for (ll i=0; i<n-1; i++)
{
node E;
scanf("%lld%lld%lld",&E.a,&E.b,&E.val);
sum+=E.val;
Q.push(E);
}
for (ll i=0; i<m; i++)
scanf("%lld",&dst[i]);
for (ll i=1; i<m; i++)
unite(dst[0],dst[i]);都以第一个点为父节点,在该集合中的点需要相互孤立
}
void kruskal()
{
ll the_max=0;//保存可取的边
while (!Q.empty())
{
node E=Q.top();
Q.pop();
ll fa=find_par(E.a);
ll fb=find_par(E.b);
if (fa != fb)//这两个点属于不同的阵营,说明这两个点肯定不会相互孤立
{
if (fb==dst[0])//如果有一个点属于需要孤立的点的集合,那么就将不需要孤立点的父节点设为dst[0]
swap(fa,fb);
unite(fa,fb);
the_max+=E.val;
continue;
}
}
printf("%lld\n",sum-the_max);
}
int main()
{
ll t;
while (~scanf("%lld",&t))
{
while (t--)
{
scanf("%lld%lld",&n,&m);
init();
kruskal();
}
}
return 0;
}