POJ 1733 Parity game(種類並查集+哈希)

Parity game

Description

Now and then you play the following game with your friend. Your friend writes down a sequence consisting of zeroes and ones. You choose a continuous subsequence (for example the subsequence from the third to the fifth digit inclusively) and ask him, whether this subsequence contains even or odd number of ones. Your friend answers your question and you can ask him about another subsequence and so on. Your task is to guess the entire sequence of numbers. 
You suspect some of your friend's answers may not be correct and you want to convict him of falsehood. Thus you have decided to write a program to help you in this matter. The program will receive a series of your questions together with the answers you have received from your friend. The aim of this program is to find the first answer which is provably wrong, i.e. that there exists a sequence satisfying answers to all the previous questions, but no such sequence satisfies this answer.

Input

The first line of input contains one number, which is the length of the sequence of zeroes and ones. This length is less or equal to 1000000000. In the second line, there is one positive integer which is the number of questions asked and answers to them. The number of questions and answers is less or equal to 5000. The remaining lines specify questions and answers. Each line contains one question and the answer to this question: two integers (the position of the first and last digit in the chosen subsequence) and one word which is either `even' or `odd' (the answer, i.e. the parity of the number of ones in the chosen subsequence, where `even' means an even number of ones and `odd' means an odd number).

Output

There is only one line in output containing one integer X. Number X says that there exists a sequence of zeroes and ones satisfying first X parity conditions, but there exists none satisfying X+1 conditions. If there exists a sequence of zeroes and ones satisfying all the given conditions, then number X should be the number of all the questions asked.

Sample Input

10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
Sample Output

3

題意大概是說給定一個N,表示接下來該二進制數最多有N位數(N〈=10^9),然後在給一個數M(M<=5000),表示接下來有M個詢問,如果接下來的詢問與上面的詢問矛盾,就輸出之前不矛盾的回合數。而每次詢問給定2個數x和y,然後後面一個字符串even或者odd,表示從第x位到第y位有偶數個1或者奇數個1(eg:1 2 even   表示第1位到第2位數之間有偶數個1)

做法:(先不考慮哈希)個人感覺就是個種類並查集。首先要把題意轉換一下,從第x位到第y位有偶數個1就可以看成x-1和y的種類相同{:::x-1和y種類相同表示從第1位到第x-1位的1的個數和從第1位到第y位的1的個數的奇偶性相同,這裏可以舉個例子:比如x=3,y=6, even 表示從3到6有偶數個1,假設第一位到第二位有奇數個1,那麼從第1位到第6位也有奇數個1,所以從第1位到第2位的1個數目的奇偶性和從第1位到第6位1的數目的奇偶性相同:::},同理可得如果x到Y有奇數個1的話表示x-1和y的種類不同,至此,上述題意已經轉換爲種類並查集。對於每位數有2個種類,當然種類是相對的。

下面是種類並查集的做法(個人總結):對於每個不同的集合首先要確立一個總的根節點,來進行參照對比,(值得注意的是:這個根節點的值必須爲0),然後每次往這個集合(定位集合1吧)加點的時候先判斷加的那個點(定爲點2)(點2所在集合設爲集合2)的種類是否和以集合1爲參照的種類相同(比如說集合1內有一個點1,如果點1和點2的種類相同,但是他們在各自集合表示的種類不同,比如點1在集合1中位物種1,點2在集合2中爲物種2,既v[1]=0,v[2]=1,v表示種類,0表示物種1,1表示物種2,此時集合1和點2所在集合的參照就不同),如果兩者的參照不同,那麼就把其中一個集合(比如集合2)的根值改變成與另一個集合(集合1)參照相同,然後通過Find函數,把該集合(集合2)所有點的值都改成與集合1參照相同的對應值,並把集合2的根值變成集合1的根值的兒子。如果兩個集合的參照本來就相同,就無需改變集合的值,只需要把集合2的根值變成集合1的根值的兒子就可以了。(這裏還需要注意的是:必須要通過路徑壓縮把所有的點變成一個根值的兒子,這是爲了防止多次變種,這也是爲什麼之前根節點必須爲0的原因)

	比如本題的話:
			1、先判斷2個點在不在同一個集合,如果在一個集合判斷2點的相對奇偶性(也就是種類) 			是否與題目所給的相同。
			2.如果不在一個集合,用Find函數找出點x-1,y的根值爲tx,ty;
			       tx......>ty
				|	|
				|	|
		                |	|		
				x-1	y
			如果x-1,y的參照種類不同,改變tx的值,然後把tx變成ty的兒子
			如果x-1,y的參照種類相同,直接把tx變成ty的兒子
			{注意路徑壓縮}

看到這是不是以爲題目就這樣了?但是你有沒有注意到位數N最大是10^9,也就是說你不能開一個這麼大數組來保存該點的父親和種類,這個問題怎麼解決呢?這裏注意到詢問只有最多隻有5000次,也就是說出現的點最多有10000個,那麼我們只需要用把這10000個數(不管其大小,只管它出現的先後順序)分別映射到從0-9999內。這種算法就是哈希,具體實現方法還是見代碼吧。
#include<stdio.h>
#include<string.h>
const int MAX = 10007;
int f[MAX],v[MAX];
int head[MAX],nxt[MAX],pnt[MAX];
int E=0;
int hash(int x) {                         //哈希映射和查找
    int h=x%MAX;
    for(int e=head[h];e!=-1;e=nxt[e])
    {
        if(pnt[e]==x) return e;
    }
    pnt[E]=x;
    nxt[E]=head[h];
    head[h]=E++;
    return E-1;
}
int Find(int x)
{
    if(f[x]!=x){
        int root=Find(f[x]);
        v[x]=(v[x]+v[f[x]])%2;          //更新集合內所有的點(集合1+集合2)
        return f[x]=root;
    }
    return x;
}
int main()
{
    int n,m;
    while(scanf("%d",&n)!=EOF)
    {
        E=0;
        memset(head,-1,sizeof(head));
        memset(nxt,0,sizeof(nxt));
        memset(pnt,0,sizeof(pnt));
        scanf("%d",&m);
        int cnt=-1,flag=1;
        for(int i=0;i<=2*m;i++)
        {
            f[i]=i;
            v[i]=0;
        }
        for(int i=0;i<m;i++)
        {
            int x,y,oe;
            char s[10];
            scanf("%d %d %s",&x,&y,s);
            x--;
            x=hash(x),y=hash(y);
            if(flag)
            {
                if(strcmp(s,"even")==0) oe=0;
                else oe=1;
                int tx=Find(x);
                int ty=Find(y);
                if(tx==ty)
                {
                    if((v[x]+v[y])%2!=oe) {flag=0;cnt=i;}       //判斷在集合內2點的相對奇偶性與題目的是<span style="white-space:pre">								</span>  否吻合
                }
                else{
                    f[tx]=ty;                           //種類並查集 更新集合2 的根節點
                    v[tx]=(v[x]+v[y]+oe)%2;
                }
            }
        }
        if(cnt==-1) cnt=m;
        printf("%d\n",cnt);
    }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章