HDU - 5438 Ponds(拓扑排序删点+并查集判断连通分量)

题目:

给出一个无向图,将图中度数小于等于1的点删掉,并删掉与他相连的点,直到不能在删为止,然后判断图中的各个连通分量,如果这个连通分量里边的点的个数是奇数,就把这些点的权值求和。

思路:

先用拓扑排序删点并更新各个点的度数,然后用并查集判断各个连通分量里边的点个数的奇偶性就ok了。

代码:

#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <iomanip>
#define MAX 1000000000
#define inf 0x3f3f3f3f
#define FRE() freopen("in.txt","r",stdin)

using namespace std;
typedef long long ll;
const int maxn = 10005;
int degree[maxn],fa[maxn],cnt[maxn],vis[maxn];
int p,m;
ll val[maxn],sum[maxn];
vector<int> mp[maxn];

void init()
{
    memset(cnt,0,sizeof(cnt));
    memset(degree,0,sizeof(degree));
    memset(sum,0,sizeof(sum));
    memset(vis,0,sizeof(vis));
    for(int i=0; i<maxn; i++)
    {
        fa[i] = i;
        mp[i].clear();
    }
}

int _find(int x)
{
    return fa[x]==x?x:fa[x] = _find(fa[x]);
}


void check()
{
    for(int i=1; i<=p; i++)
    {
        printf("%d ",degree[i]);
    }
    printf("\n");
}

int main()
{
    //FRE();
    int kase;
    scanf("%d",&kase);
    while(kase--)
    {
        init();
        scanf("%d%d",&p,&m);
        for(int i=1; i<=p; i++)
        {
            scanf("%lld",&val[i]);
        }
        for(int i=0; i<m; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            degree[u]++;
            degree[v]++;
            mp[u].push_back(v);
            mp[v].push_back(u);
            u = _find(u);//将同一个连通分量里边的点连接
            v = _find(v);
            if(u!=v)
                fa[u] = v;
        }
        //check();
        queue<int> que;
        for(int i=1; i<=p; i++)
        {
            if(degree[i]<=1)
                que.push(i);
        }
        while(!que.empty())//利用拓扑排序删点
        {

            int u = que.front(); que.pop();
            //cout<<u<<endl;
            vis[u] = 1;
            for(int i=0; i<mp[u].size(); i++)
            {
                degree[mp[u][i]]--;
                if(!vis[mp[u][i]] && degree[mp[u][i]]<=1)
                    que.push(mp[u][i]);
            }
        }
        //check();
        ll ans = 0;
        for(int i=1; i<=p; i++)
        {
            if(degree[i]<=1) continue;
            int r = _find(i);
            cnt[r]++;//统计该连通分量里边的点的个数
            sum[r] += val[i];//该连通分量的权值的和
        }
        for(int i=1; i<=p; i++)
        {
            if(cnt[i]%2)
            {
                ans += sum[i];
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2019-03-15 17:29  sykline  阅读(232)  评论(0编辑  收藏  举报