hdu3367 Pseudoforest(最大生成树+伪森林)


http://acm.hdu.edu.cn/showproblem.php?pid=3367

题意:给你一个森林和一些边,求它的最大生成树。这个生成树里允许至多有一个环。


思路:本题点数过多,不能用Prim。至于最大生成树,其实就是改变下排序顺序而已。关键是对伪森林的操作,允许至多有一个环。刚开始想的添加一个节点就判断是否可以构成环,然后能构成环的话把这个边存起来往复求最大值,最后找到最大值的边即为伪森林所需的最大边。


接着就开始WA。没思路,看别人的,对MST的理解更进了一步。所谓生成MST的过程,其实就是森林变成树的过程,原先没环直接合并即可。现在有环,那么合并的两个节点就要分情况讨论了:

(1)、如果这两个节点不在同一棵树,看这两个节点所在的树中是否有环。初始设置所有树均为无环树。如果都没有,那么直接合并;如果有一个,合并后把这个树置为有环树;如果都有,那么不满足至多一个环,不合并。

(2)、如果这两个节点不在同一棵树,看这两个节点所在的树中是否有环。初始设置所有树均为无环树。如果都没有,那么直接合并,并且置为有环树;如果有一个或其他,那么不满足至多一个环,不合并。



#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>

using namespace std;

typedef long long LL;

const int N = 10005;
const int INF = 0x3f3f3f3f;

int pre[N], n, ednum, sum;
bool circle[N];

struct node
{
    int u, v, w;
}edge[100005];

bool cmp(node x, node y)
{
    if(x.w>y.w) return true;
    else return false;
}

int Find(int x)
{
    int r = x;
    while(r != pre[r])
        r = pre[r];
    int i = x, j;
    while(pre[i] != r)
    {
        j = pre[i];
        pre[i] = r;
        i = j;
    }
    return r;
}

void Union(int p1, int p2, int w)
{
    int x = Find(p1);
    int y = Find(p2);
    if(x != y)//不在同一棵树上
    {
        if(circle[x]==false && circle[y]==false)//两棵树都没有环
        {
            pre[x] = y;
            sum+=w;
        }
        else if((circle[x]==true&&circle[y]==false) || (circle[x]==false&&circle[y]==true))//一棵树有环
        {
            pre[x] = y;
            sum+=w;
            circle[y] = true;
        }
    }
    else//在同一棵树上
    {
        if(circle[x]==false && circle[y]==false)//两棵树都没有环
        {
            pre[x] = y;
            sum+=w;
            circle[y] = true;
        }
    }
}

void kruskal()
{
    for(int i = 0; i < n; i++)//起点坐标可以是0
    {
        pre[i] = i;
    }
    memset(circle, false, sizeof(circle));
    sort(edge+1, edge+1+ednum, cmp);
    for(int i = 1; i <= ednum; i++)
    {
        Union(edge[i].u, edge[i].v, edge[i].w);
    }
}


int main()
{
  //  freopen("in.txt", "r", stdin);
    int u, v, w;
    while(~scanf("%d%d", &n, &ednum))
    {
        if(n==0 && ednum==0) break;
        sum = 0;
        for(int i = 1; i <= ednum; i++)
        {
            scanf("%d%d%d", &u, &v, &w);
            edge[i] = (struct node){u, v, w};
        }
       // prim();
        kruskal();
        printf("%d\n", sum);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值