[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

加强印象,仔细看图:

[wikioi]关押罪犯

本题的思路有两种,一是求最大值最小,而已二分+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\n", ans);

    return 0;

}

  

 

你可能感兴趣的:(IO)