HDU 3367:Pseudoforest

来源:互联网 发布:虎扑认证淘宝店铺 编辑:程序博客网 时间:2024/05/18 03:24


Pseudoforest

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2897    Accepted Submission(s): 1139


Problem Description
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 30 1 11 2 12 0 14 50 1 11 2 12 3 13 0 10 2 20 0
 

Sample Output
35
 

pesudo(虚伪的,假的) forest(森林) 伪森林。 

给出N个点M条边。求一个最大的连通子图(有条件,这个图中最多只能有一个环),所谓最大是图中边的权值加起来要尽可能大。

对M条边按权值从大到小排序。对于一条边只要它加到我当前的图中满足新的图中环的个数<=1,那就直接加入。

用并查集实现。

遍历到边u-v,查找u和v所属的集合。并设u所属的集合是fatheru,顶点v所属的集合是fatherv。

情况1:fatheru = fatherv,两个节点同属于一个集合:father = fatheru = fatherv

则说明节点u与节点father连通,节点v与节点father连通,如果当前father这个集合不存在环,则我要选择u-v这条边。

选择了这条边后,u与father连通,v与father连通,且u与v连通,说明加上u-v这个边形成一个环,则标记father这个

集合,代表它有环了。如果father本身就已经有环了,我们不能选择u-v这个边,因为加上u-v后,就会有两个环了(不符合题意)

情况2:fatheru != fatherv。两个节点不属于同一个集合。

如果两个集合都已经存在环了,则一定不能合并。

如果两个集合中都没有环,则直接合并。

如果其中一个集合有环,一个集合没有环,也可以合并。


#include<iostream>#include<stdio.h>#include<algorithm>using namespace std;const int maxn = 10002;int N,M;   ///N个节点M条边struct Edge{    int u;    int v;    int c;}edge[100000+5];int father[maxn]; ///并查集int vis[maxn];    ///vis用来标记一颗分离集合树存不存在环。bool cmp(struct Edge e1,struct Edge e2) ///边的权值按照从大到小排序{    return e1.c>e2.c;}void Make_Set(){    for(int i = 0; i < N; i++)    {        vis[i] = 0;        father[i] = i;    }}int Find_Set(int x){    while(x != father[x])        x = father[x];    return x;}int main(){    int i,ans;    while(~scanf("%d%d",&N,&M))    {        if(N == 0 && M == 0) break;        ans = 0;        for(i = 0; i < M; i++)            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].c);        sort(edge,edge+M,cmp);  ///边按权值从大到小排序        Make_Set();        for(i = 0; i < M; i++)        {            int u = edge[i].u;            int v = edge[i].v;            int c = edge[i].c;            u = Find_Set(u);            v = Find_Set(v);             /**即使节点edge[i].u和节点edge[i].v在一个集合中,只能说明edge[i].u与u连通。            edge[i].v与u连通,如果分离集合树u不连通,我们加上u,v这条边,使它连通,则以后在            遇到edge[i].u和edge[i].v同在一个集合的情况,就不用考虑了,因为题目要求连通图只            能有一个环**/            if(u == v)             {                if(vis[u]==1) continue;                else                {                    vis[u] = 1;                    ans += c;                }            }            else              {                ///如果两个集合都已经存在一个环,不能合并                if(vis[u] && vis[v])                     continue;                else if(!vis[u] && !vis[v]) ///都没有环,合并                {                    father[u] = v;                    ans += c;                }                else if(!vis[u]) ///u没有环,合并                {                    father[u] = v;                    ans += c;                }                else if(!vis[v]) ///v没有环,合并                {                    father[v] = u;                    ans += c;                }            }        }        printf("%d\n",ans);    }    return 0;}



0 0