POJ1733 Parity game带权离散化并查集

POJ1733 Parity game

——————带权离散化并查集


算法小白在网上看了很多的解题方式

但是很多都只有一个大致思路

但对于我还有一些初学者来说,特别难理解

所以我找了网上的一篇比较易懂的解题方法

但用自己的代码重新实现

并且加上详细注释


代码如下

//https://blog.csdn.net/u013480600/article/details/21128299 很好的解释(离散化)
//https://blog.csdn.net/dominating413421391/article/details/47103281 
//https://blog.csdn.net/dominating413421391/article/details/47100515 类似题POJ1962
//带权并查集+离散化+合理的路径压缩
//用奇+奇的偶的思想来做,特别的相减也是一样的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
#include<stdlib.h>

using namespace std;

const int maxn = 2*5e3+10;//因为串的长度特别长,不能存
int len,m;//长度和命令     //但是我们发现,数据只有5e3,即有效的节点只有2*5e3个
char cmd[10];            //所以我们只存代表区间的端点
int father[maxn],to_rel[maxn]; //存父节点,和i点与其父节点的奇偶
map<int,int> point;

void init();//初始化
int find(int);//找父节点
bool Uion(int,int,int);//合并两个节点,返回值代表是否冲突

int main()
{
    // freopen("./in.txt","r",stdin);
    int index = 0,ans = 0,flag = 0;//离散化节点,ans代表错误的行
    int odd_even; //奇偶位
    init();//初始化
    scanf("%d%d",&len,&m);
    for(int i = 0;i < m;i++)
    {
        int a,b; scanf("%d%d%s",&a,&b,&cmd);
       // printf("%d %d %s\n",a,b,cmd);
        if(flag) continue;
        a--;//我们将区间化为(a,b]半开半闭区间,好让区间首位相接 ps(1,2)->(0,2] (3,4)->(2,4]可以由1链接到2->4
        if(point.find(a) == point.end()) point[a] = index++; //如果没有找到,说明是一个新的点
        if(point.find(b) == point.end()) point[b] = index++;
        int x = point[a], y = point[b]; //获取离散化之后的下标
        if(cmd[0] == 'e')  odd_even = 0;//偶数
        else odd_even = 1;
        if(!Uion(x,y,odd_even)) flag = 1;//此处与前面矛盾
        else ans++;
    }
    printf("%d\n",ans);
    // freopen("CON","r",stdin);
    // system("pause");
    return 0;
}

void init(){
    for(int i = 0;i < maxn;i++){
        father[i] = i;
        to_rel[i] = 0;//开始全部不确定所以全部都初始化为0
    }
}

int find(int x)  //寻找根节点并且路径压缩+奇偶数组更新,每次都是在Uion中更新
{
    if(x == father[x]) return x;
    else{
        int rel = father[x];//x的原先根节点
        father[x] = find(father[x]);//必须在此位置,不能放到最后,从里到外一层层更新
        to_rel[x] = (to_rel[x]+to_rel[rel])%2;//奇数+奇数=偶数 0是偶数
        return father[x];
    }
}

bool Uion(int x,int y,int d)
{
    int fx = find(x);//找到与x同集合的根
    int fy = find(y);//同上
    if(fx == fy) //说明在一个集合中,可以判断命令是否正确
    {
        if((to_rel[x]+to_rel[y])%2 == d) return true;
        //x到y的奇偶性=x->root+y->root%2画图就可以理解了
        else return false;
    }
    else{//说明无法判断,更新数据
        father[fx] = fy;//更新父节点 ps:fx的子节点对fx的距离在下次find的时候更新
        to_rel[fx] = (to_rel[x]+to_rel[y]+d)%2;//画图以便理解,to_rel[x/y]都是x,y到自己根的距离
    }
    return true;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章