POJ1733 Parity game

Parity game

Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 10295Accepted: 3955

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

算法设计

该题目采用带权并查集来实现,如果对并查集不了解,可以参考并查集

Hash操作

首先由于子串长度比较大,但是输入只有5000,所以至多设计到10000个位置,可以将输入做hash,映射到10000范围内。伪代码如下:

int toMao = 0//已经映射的节点个数
int mapVlaue[10000],originalValue[10000],next[10000]
initial mapValue with -1
hashMap(int a)
int map = a % 10000
int value = mapValue[map]
while(value != -1){
//如果找1的位置说明该点还没有值与之对应,可以作为map之后的值
if(originalValue[value] == a){//该值之前已经做过hash操作
return value}
value = next[value]
}
originalValue[toMap] = a;//储存原始值
next[toMap] = mapValue[map]//储存上一次map之后数值的位置
mapValue[map] = toMap ++//储存该值map之后数值的位置
return toMap – 1

由于已经做了hash,之后区间使用[a,b]表示。使用带权并查集,如果区间[a,b]已经确定关系则a为b节点的父亲,权值为1代表有奇数个1,权值为0代表有偶数个1。

find操作

find时需要进行路径压缩,如图 1左图所示,x和y为0或1,执行find后变为图 1右图,此时需要更新c与a的关系。

图 1 查找时更新关系
当[a,b]中有偶数个1,即x为0。则[a,c]中1的个数的奇偶性取决于[b,c]中1的个数的奇偶性。
当[a,b]中有奇数个1,即x为1。则[a,c]中1的个数的奇偶性与[b,c]中1的个数的奇偶性恰好相反。
综上即r[a,b]为a到b中1的个数的奇偶性,则有r[a,c] = r[a,b] ^ r[b,,c]

Union 操作

首先需要判断是否原先已经囊括该区间,即[a,b]中a和b是否有共同的父节点,如果有则需要判断是否冲突。如图 2所示

图 2 判断冲突图解
此时可以知道[a,b]和[a,c]区间中1的个数的奇偶性,需要获取[b,c]区间的奇偶性,即为上述过程的逆操作,即 z = x ^ y,已知z和x求y,根据异或的性质,易知y = z ^ x。所以r[b,c] = r[a,b] ^ r[a,c]
如果没有拥有相同的父节点,则说明之前没有囊括这个区间,那么需要对该区间进行合并。如图 3所示已知区间[fa,a]、[fb,b]、[a,b]中1的个数的奇偶性,要求 [fa,fb]区间中1的个数的奇偶性,

图 3 合并图解
可以看到,记n[a,b]为[a,b]中1的个数,则n[fa,fb] = n[fa,a] + n[a,b] – n[fb,b]所以有表1所示关系表。
表 1 合并关系表

[fa,a][fb,b][a,b][fa,fb]
奇数奇数奇数奇数
奇数奇数偶数偶数
奇数偶数奇数偶数
奇数偶数偶数奇数
偶数奇数奇数偶数
偶数奇数偶数奇数
偶数偶数奇数奇数
偶数偶数偶数偶数

可以看到当有奇数个1的区间为奇数时,[fa,fb]为奇数,否则为偶数所以有
r[fa,fb] = r[fa,a] ^ r[fb,b] ^ r[a,b]
main 函数伪代码

int main(){
    int m,n;
    int count = 0;
    scanf("%d%d",&m,&n);
    init();
    for(int i = 0;i < n;i ++){
        int a,b;
        char s[5];
        scanf("%d%d%s",&a,&b,s);
        int isodd = 0;
        if(s[0] == 'o'){
            isodd = 1;
        }
        if(!Union(hashMap(a-1),hashMap(b),isodd)){
            break;
        }
        count ++;
    }
    printf("%d",count);
    return 0;
}

代码实现

#include <stdio.h>
#include <cstring>

const int num = 10000;
int father[num],isOdd[num],origunalValue[num],mapValue[num],next[num],toMap;
void init();
int find(int search);
bool Union(int a,int b,int n);
int hashMap(int a);
int main(){
    int m,n;
    int count = 0;
    scanf("%d%d",&m,&n);
    init();
    for(int i = 0;i < n;i ++){
        int a,b;
        char s[5];
        scanf("%d%d%s",&a,&b,s);
        int isodd = 0;
        if(s[0] == 'o'){
            isodd = 1;
        }
        if(!Union(hashMap(a-1),hashMap(b),isodd)){
            break;
        }
        count ++;
    }
    printf("%d",count);
    return 0;
}
//map a integer from 0 to 1_000_000_000 to a unique integer from 0 to 10000
int hashMap(int a){
    int map = a % num;
    int value = mapValue[map];
    //if the map value has been used
    while(value != -1){
        //the map value has been defined
        if(origunalValue[value] == a){
            return value;
        }
        value = next[value];
    }
    //record the original value
    origunalValue[toMap] = a;
    //update the next index to be mapped
    next[toMap] = mapValue[map];
    //record the value after being mapped
    mapValue[map] = toMap ++;
    return toMap - 1;
}
void init(){
    toMap = 0;
    memset(mapValue,-1, sizeof(mapValue));
    for(int i = 0;i < 10000;i ++){
        father[i] = i;
        isOdd[i] = 0;
    }
}
int find(int search){
    if(father[search] != search){
        int temp = father[search];
        father[search] = find(temp);
        /*if the number of 1 from search to temp and temp to its father are both odd or even, the number of 1
        between search to its father are even ,otherwise are odd.*/
        isOdd[search] ^= isOdd[temp];
    }
    return father[search];
}
bool Union(int a,int b,int n){
    int fa = find(a);
    int fb = find(b);
    if(fa == fb){
        //if(isOdd[a] == -1 || isOdd[b] == -1){return true;}
        return n == isOdd[a] ^ isOdd[b];
    }
    else{
        father[fb] = fa;
        isOdd[fb] = isOdd[b] ^ isOdd[a] ^ n;
    }
    return true;
}

复杂度分析

假设输入规模为n,输入数量为m,则构建并查集的复杂度为O(n),如果和前面的由冲突那么通过find操作可以进行判断,前面说到find的摊还复杂度为α(n),如果不在同一个不相交集合,那么可以使用union进行合并,union的摊还复杂度为α(n),所以整个程序的复杂度为n+mα(n)。

编程技巧

该题目解题思路和之前的一样,但是对带权并查集的表示却有所不同,食物链中采用取余的方式来进行判断,这里由于只有两种关系,所以采用0和1表示更为方便。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值