hdu 3367 Pseudoforest kruskal算法的变种+并查集

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=10010;
const int maxm=100010;
int f[maxn],vis[maxn];
struct node{
    int a,b,w;
}e[maxm];
int find(int x)
{
    if(x!=f[x])f[x]=find(f[x]);
    return f[x];
}
int cmp(node a,node b)
{
    return a.w>b.w;
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)break;
        int i,j,k,x,y,z,num=0,ans=0,aa,bb,t,p=0;
        for(i=0;i<n;i++)
        {
            f[i]=i;
            vis[i]=0;//用于标记成环的并查集集合最高父节点,即find()的点。
        }
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            e[i].a=x;
            e[i].b=y;
            e[i].w=z;
        }
        sort(e,e+m,cmp);
        for(i=0;i<m;i++)
        {
            aa=find(e[i].a);
            bb=find(e[i].b);
            if(aa==bb)
            {
                if(!vis[aa])
                {
                    vis[aa]=1;
                    ans+=e[i].w;
                }
            }
            else
            {
                if(!vis[aa]||!vis[bb])
                {
                    if(!vis[aa]&&vis[bb])
                    {
                        f[aa]=bb;
                        ans+=e[i].w;
                    }
                    else if(!vis[bb]&&vis[aa])
                    {
                        f[bb]=aa;
                        ans+=e[i].w;
                    }
                    else
                    {
                        f[aa]=bb;
                        ans+=e[i].w;
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
/*
    题意:给你一个n结点的图,所有的结点不一定都连通,即存在多个连通图(如同森林中有很多树一样)。
    现在求一个连通子图,使得边权的和最大,且每个连通图最多存在一个环。
    
    一开始的想法是用kruskal算法求出个最大生成树,再在每个连通图上加最大的未用边。当然这种wa死。。虽然还是不知道为什么错。
    
    正解是同样用kruskal算法,不过每次用并查集判断是否在同个集合外,还要判断,两边是否有成环。
    成环情况有,一边又一边没有,两边都没有,两边都有;前两个都能相连,第三个就不可以。
*/

你可能感兴趣的:(最小生成树)