Pseudoforest HDU - 3367(最大生成树+kruskal算法+并查集+判环)

本文解析了Ingraph theory中的伪森林概念,介绍了如何在给定图中寻找总权值最大的伪森林,通过kruskal算法的变种处理环的存在问题。核心在于理解连通分量和环限制,以及如何合并树结构以保持伪森林性质。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

In graph theory, a pseudoforest is an undirected graph in which every connected component has at most one cycle. The maximal pseudoforests of G are the pseudoforest subgraphs of G that are not contained within any larger pseudoforest of G. A pesudoforest is larger than another if and only if the total value of the edges is greater than another one’s.

Input

The input consists of multiple test cases. The first line of each test case contains two integers, n(0 < n <= 10000), m(0 <= m <= 100000), which are the number of the vertexes and the number of the edges. The next m lines, each line consists of three integers, u, v, c, which means there is an edge with value c (0 < c <= 10000) between u and v. You can assume that there are no loop and no multiple edges.
The last test case is followed by a line containing two zeros, which means the end of the input.

Output

Output the sum of the value of the edges of the maximum pesudoforest.

Sample Input

3 3
0 1 1
1 2 1
2 0 1
4 5
0 1 1
1 2 1
2 3 1
3 0 1
0 2 2
0 0
Sample Output
3
5

题意

给出一个图,求出最大的伪森林(伪森林就是指这个图的一个子图,这个子图的每个连通分量中最多只能有一个环,并且这个子图的所有权值的和为最大)

思路

先了解一下最大生成树 什么是最大生成树?

在一个图的所有生成树中边权值和最大的生成树即为最大生成树

最大生成树算法和最小生成树的算法类似,用kruskal算法求最小生成树时,是先把边的权值从小到大排序,每次选择最小的边,再判断是否可以加入生成树

最大生成树就是将边的权值从大到小排序,每次选择最大的边,再去判断这条边是否可以加入最大生成树中即可

但是这个题没有直接用最大生成树算法,和最大生成树算法类似,稍做了一些改变,因为每个连通分量可以有一个环,所以,我们在合并两棵树时,要判断这两棵树是否有环。

如果这两棵树都有环,那么则不可以合并这两棵树,因为只允许有一个环

如果两棵树之中有一棵树有环,就可以合并,然后给没有环的那棵树做标记,表示合并之后就有环了

如果两棵树都没有环,就可以直接合并两棵树

如果有两个点是同一棵树上的,判断这棵树上是否有环,如果没有环的话,这棵树上可以有一个环,就可以将这两个点连接,建立一个环,标记有环

代码

#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[100010],cycle[100010];
int sum;
struct node
{
    int u,v,w;
} s[100010];
bool cmp(node x,node y)
{
    return x.w>y.w;//从大到小排序
}
void init()
{
    for(int i=0; i<100010; i++)
    {
        f[i]=i;
        cycle[i]=0;//记录是否有环,1表示有,0表示无
    }
}
int getf(int v)
{
    if(f[v]==v)
        return v;
    else
        return f[v]=getf(f[v]);
}
void kruskal()
{
    int x,y;
    sum=0;
    for(int i=0; i<m; i++)
    {
        x=getf(s[i].u);
        y=getf(s[i].v);
        if(x!=y&&(cycle[x]!=1||cycle[y]!=1))//如果一棵树有环,可以合并
        {
            sum+=s[i].w;
            f[x]=y;
            if(cycle[x])//合并要注意标记
                cycle[y]=cycle[x];
        }
        else if(!cycle[x])//当两个点都在一棵树上时,这棵树添加一条边构成环
        {
            sum+=s[i].w;
            cycle[x]=1;//标记这棵树有环,1表示有环
        }
    }
}
int main()
{
    while(cin>>n>>m)
    {
        init();
        if(n==0&&m==0)
            break;
        for(int i=0; i<m; i++)
            cin>>s[i].u>>s[i].v>>s[i].w;
        sort(s,s+m,cmp);
        kruskal();
        cout<<sum<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值