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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章