1. 程式人生 > >[HDU3367] Pseudoforest [最大生成環套樹森林][並查集]

[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的應該也就我了(