[HDU3367] Pseudoforest [最大生成环套树森林][并查集]

[ L i n k \frak{Link} ]


题意:求图的最大生成环套树森林的边权和。 n 1 0 4 , m 1 0 5 \frak{n \le 10^4, m \le 10^5}

一开始想的是枚举连通分量分别 K r u s k a l \frak{Kruskal}
当然这是错的,因为就算整个图连通也可以选成森林,并且后者可能比前者得到的答案要优。

但是这样要怎么维护?选择要不要连成森林的好像有点难。
考虑到 m s t \frak{mst} 两个算法都是贪心,这个可不可以贪心地选?
从大到小,除非加一个边会让一个子图带两个环就选进去?
现在有一条边满足条件,如果它会成一个环,那就加上,把这个集合标记不能再成环。
如果不成环,那也要加上。
两种情况正确性都挺显然的。

什么样子的边满足条件?
1.不成环
2.当前未成环,成一个环

于是考虑:
1.加入当前边,不成环,是否连接两个环
2.加入当前边,成环,当前连通分量是否原来就有环


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
using namespace std;
int fa[10005], n, m, vis[10005], ans;
bool rin[10005];
int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y, int w) {
    int fx = find(x);
    int fy = find(y);
    if (fx == fy) {
    	if (rin[fx]) return;
    	ans += w;
    	rin[fx] = 1;
    	return;
    }
    if (rin[fy] && rin[fx]) return;
    fa[fx] = fy;
    rin[fy] |= rin[fx];
    ans += w;
}
struct sut {
    int u, v, w;
    bool operator < (const sut &b) const {
        return w > b.w;
    }
}E[100005];
int main() {
    while (~scanf("%d%d", &n, &m)) {
        if (!n && !m) return 0;
        ans = 0;
        memset(rin, 0, sizeof(rin));
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= n; ++i) {
            fa[i] = i;
        }
        for (int i = 1; i <= m; ++i) {
            scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
            ++E[i].u; ++E[i].v;
        }
        sort(E+1, E+1+m);
        for (int i = 1; i <= m; ++i) {
            merge(E[i].u, E[i].v, E[i].w);
        }
        printf("%d\n", ans);
    }
    return 0;
}

cmp函数忘记写return的应该也就我了(

猜你喜欢

转载自blog.csdn.net/Estia_/article/details/83821058