hdu 4313 - Matrix(最小生成树,并查集)

题目:

Matrix


题意:

N个点N-1条无向边,K个数,要求去掉一些边,使得这K个数相互不可达,求去掉的边的权值和的最小值


思路:

这K个数不能相互到达,说明这k个数分别属于不同的集合,我们把可以放进同一集合的数作为同一集合,不能放进的,则把这条边舍弃。集合自然想到用并查集。要找最小值,我们可以把所有边按权值从大到小排序,顺序寻找,找到不能放进的则跳过,res+=w,能放进的则进行合并(由于我们其实是找的能够合并的边,只是顺路找不能合并的,所以从大到小排序,大的先合并了,剩余的不能合并的就小了) , 取权值构造图,自然想到最小生成树。


代码:

克鲁斯卡尔算法:

//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 522133279;
const int MAXN = 1000000+100;
#define eps 1e-14
const int mod = 100000007;

int set[100000+100];

struct edge
{
    int s;
    int e;
    int w;

    bool operator <(const edge&b)const
    {
        return w > b.w;
    }
} e[100000+100];

int des[100000+100];                //1 - 是剔除点 0 - 非剔除点
int n,k;

int find(int x)
{
    return set[x] != x ? set[x] = find(set[x]) : x;
}

int merger(int x , int y)
{
    return ((x = find(x)) != (y = find(y))) && (set[x]=y);
}

LL ckru(int n)
{
    sort(e,e+n-1);

    LL res = 0;

    for(int i = 0 ; i < n-1 ; i++)
    {
        int u = find(e[i].e);
        int v = find(e[i].s);


        if(!des[u] || !des[v])          //如果不是两个都是剔除点,则合并,注意正常点应该被剔除点合并
        {
            if(des[v])
                merger(u,v);
            else if(des[u])
                merger(v,u);
            else
                merger(u,v);
        }

        else if(des[u] && des[v])       //如果两个都是剔除点,那么这条边不应该被加进去,也就是它应该被销毁
            res += e[i].w;
    }

    return res;
}


int main()
{
    //freopen("in","r",stdin);
    //freopen("out","w",stdout);

    int t;
    scanf("%d" , &t);

    while(t--)
    {
        memset(des,0,sizeof(des));

        scanf("%d%d",&n,&k);

        for(int i = 0 ; i <= n ; i++)
            set[i]=i;

        for(int i = 0 ; i <  n-1 ; i++)
            scanf("%d%d%d" , &e[i].s,&e[i].e,&e[i].w);

        for(int i = 0 ; i < k ; i++)
        {
            int tmp;
            scanf("%d" , &tmp);
            des[tmp]=1;
        }

        printf("%I64d\n", ckru(n));
    }

    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值