[wikioi]关押罪犯

错误半天还是因为并查集写错了。写错的地方是合并X和Y的时候,应该把FX挂到FY上去,而不是把X挂到Y上或FY上去,因为FX和FY下面有一树别的节点。 http://www.nocow.cn/index.php/%E5%B9%B6%E6%9F%A5%E9%9B%86

加强印象,仔细看图:

本题的思路有两种,一是求最大值最小,而已二分+BFS来做,但效率较低。更好的做法是贪心+并查集。先按照怨念排序,然后从大到小判断是否有冲突。

判断冲突的时候用了个巧妙的方法,就是A和B不能放在一组,那么设A的补集为!A,B的补集!B,那么merge(A, !B) 且merge(B, !A),这样当有A不能和B在一起,C不能和B在一起时,由于A和C都在!B一组里了,merge时会冲突。i的补集用i+n来表示,所以开2N的数组。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <memory.h>
#define MAX(a, b) a>b?a:b
#define LEN_N 20005
#define LEN_M 100005
using namespace std;

struct Conflict
{
    int a;
    int b;
    int val;
};

int N;
int M;
int father[LEN_N*2];
Conflict edges[LEN_M];

bool comp(Conflict a, Conflict b)
{
    return a.val > b.val;
}

int find(int i) {
    if (father[i] != i) {
        father[i] = find(father[i]);
    }
    return father[i];
}

void merge(int i, int j) {
    father[find(i)] = find(j);
}

void init()     
{
    memset(edges, 0, sizeof(edges));
    memset(father, 0, sizeof(father));
    scanf("%d%d", &N, &M);
    for (int i = 0; i < M; i++) {
        scanf("%d%d%d", &edges[i].a, &edges[i].b, &edges[i].val);
    }
    for (int i =1; i <= N*2; i++) {
        father[i] = i;
    }
}     

int main()
{
    init();
    sort(edges, edges+M, comp);
    int ans = 0;
    for (int i = 0; i < M; i++) {
        int a = edges[i].a;
        int b = edges[i].b;
        if (find(a) == find(b)) {
            // conflict
            ans = edges[i].val;
            break;
        }
        merge(a, b+N);
        merge(b, a+N);
    }
    
    printf("%d
", ans);
    return 0;
}

  

原文地址:https://www.cnblogs.com/lautsie/p/3385140.html