POJ1182---食物链(带权并查集~技巧性超强的解法)

来源:互联网 发布:火车票网络退票手续费 编辑:程序博客网 时间:2024/06/10 16:28

参考:《挑战程序设计竞赛》

这本大佬写的书上面的办法,并没有去刻意的去保存权,来回地更改权值,已达到修改捕食关系的目的。详解如下:

前面的输入的决定了后面的正确与否,前面的输入具有不确定性,这点是题目的要求:“ 当前的话与前面的某些真的话冲突,就是假话; ”
思路:
第一组假设是D为1的情况,也就是两个是同一类的情况,反正没说是这个同一类属于ABC中的哪一类,所以有三种可能,把这三种可能都压进去。怎么压呢?核心来了!
开辟一个3*N数组,1…N代表A类,N+1…2N代表B类,2N+1…3N代表C类。
1)第一种,x和y属于同一种类———合并x-A和y-A、x-B和y-B、x-C和y-C。

2)第二种,x吃y—————————合并x-A和y-B、x-B和y-C、x-C和y-A。

压入的时候,要先判断是否这句话为假话。假如说输入了第一种情况 x,y是否同一类,要判断x-A和y-B,X-A和y-C是否在集合里了,
为什不判断x-B和y-C,x-B和y-A; x-C和y-A,x-C和y-B?
因为压入的时候,就是按照三种可能压入的,所以只需判断一种情况就行了,另外两种不用判断。

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;int s[150300];int Find(int x){    if (s[x] < 0)return x;    else return s[x]= Find(s[x]);//路径压缩}void unite(int x, int y){    int rootx = Find(x);    int rooty = Find(y);    /*if (s[rootx] < s[rooty])//按规模归并,额,为什么在这里用会超内存?坐等大佬解释???    {        s[rootx] += s[rooty];        s[rooty] = rootx;    }    else    {        s[rooty] += s[rootx];        s[rootx] = rooty;    }*/    if (rootx != rooty)        s[rootx] = rooty;}bool same(int x, int y){    return Find(x) == Find(y);}int main(){    int N, K, D, x, y, ans = 0;    cin >> N >> K;    fill(s+1, s+3*N+1, -1);    while(K--)    {        scanf("%d%d%d", &D, &x, &y);        //cin >> D >> x >> y;   //用cin会超时~~~没骗你        if (x > N || y > N) { ans++; continue; }        if (D == 1)        {            if (same(x, y + N) || same(x, y + 2 * N))                ans++;            else            {                unite(x, y);                unite(x + N, y + N);                unite(x + 2 * N, y + 2 * N);            }        }        else        {            if (same(x, y) || same(x, y+2*N))                ans++;            else            {                unite(x, y + N);                unite(x + N, y + 2 * N);                unite(x + 2 * N, y);            }        }    }    cout << ans<<endl;    system("pause");}