HDU 4313 Matrix

题意:  n个点的一棵树,其中有k个敌人所在结点,要破坏一些边使得这 k 个点互不可达,求出破坏边的最小权值和。

分析:  对于有n个结点的树,删除任意的k (k<=n-1)条边都能将原树分成k+1个部分

          按照题意至少需要将原树划分成 k 个部分(此时每部分中都包含一个敌人的点),删除的边数为k-1。

           类似kruskal最小生成树的过程,不过此处将边按权值从大到小排列,每次将边加进来时要判断是否会使两个危险的

           点连通,是的话这条边就是需要被删除的,否则将它加到树上。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define maxn 100005
#define clr(x)memset(x,0,sizeof(x))
int n,m;
int u[maxn],v[maxn],w[maxn];
int r[maxn],f[maxn];
int find(int x)
{
    return f[x]==x?x:(f[x]=find(f[x]));
}
void join(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
    {
        if(fx<fy)
            f[fy]=fx;
        else f[fx]=fy;
    }
}
bool cmp(int a,int b)
{
    return w[a]>w[b];
}
int pos[maxn];
__int64 kruskal()
{
    int i;
    __int64 res=0;
    for(i=0;i<n;i++)
        f[i]=i;
    for(i=0;i<n-1;i++)
        r[i]=i;
    sort(r,r+n-1,cmp);
    for(i=0;i<n-1;i++)
    {
        int e=r[i];
        int fx=find(u[e]);
        int fy=find(v[e]);
        if(pos[fx]&&pos[fy])
        {
            res+=w[e];
            continue;
        }
        if(pos[fx]||pos[fy])
            pos[fx]=pos[fy]=1;
        join(fx,fy);
    }
    return res;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        int i,a,b,c;
        for(i=0;i<n-1;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            u[i]=a;
            v[i]=b;
            w[i]=c;
        }
        clr(pos);
        for(i=0;i<m;i++)
        {
            int tmp;
            scanf("%d",&tmp);
            pos[tmp]=1;
        }
        printf("%I64d\n",kruskal());
    }
    return 0;
}

 

posted @ 2012-09-15 11:40  'wind  阅读(311)  评论(0编辑  收藏  举报