对于有n个结点的树,容易证明删除任意的k (k<=n-1)条边都能将原树切成k+1个部分。按照题意至少需要将原树划分成d个部分(此时每部分中都包含一个危险的点),删除的边数为d-1。
贪心算法:类似kruskal最小生成树的过程,不过此处将边按权值从大到小排列,每次将边加进来时要判断是否会使两个危险的点连通,是的话这条边就是需要被删除的,否则将它加到树上。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int ac[110000],father[110000];
__int64 sum;
struct hello
{
int x,y,z;
}yi[110000];
bool cmp(hello t1,hello t2)
{
return t1.z>t2.z;
}
int find(int x)
{
if(x!=father[x])
father[x]=find(father[x]);
return father[x];
}
void add(int x,int y,int z)
{
x=find(x);
y=find(y);
if(ac[x]&&ac[y])
return ;
else
{
if(ac[x])
{
father[y]=x;
}
else
father[x]=y;
sum+=z;
}
}
int main()
{
int a,b,n,m,ncase,i;
scanf("%d",&ncase);
while(ncase--)
{
__int64 max=0;
sum=0;
scanf("%d%d",&n,&m);
memset(ac,0,sizeof(ac));
for(a=0;a<=n;a++)
father[a]=a;
for(a=0;a<n-1;a++)
{
scanf("%d%d%d",&yi[a].x,&yi[a].y,&yi[a].z);
max+= yi[a].z;
}
for(a=0;a<m;a++)
{
scanf("%d",&b);
ac[b]=1;
}
sort(yi,yi+n-1,cmp);
for(a=0;a<n-1;a++)
add(yi[a].x,yi[a].y,yi[a].z);
printf("%I64d\n",max-sum);
}
}