CQOI2013 新nim游戏题解

it2026-03-27  9

传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。

本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。

如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。

 

【输入格式】

第一行为整数k。即火柴堆数。第二行包含k个不超过109的正整数,即各堆的火柴个数。

 

【输出格式】

输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1。

 

【输入输出样例】

nim.in

nim.out

6

5 5 6 6 5 5

21

nim.in

nim.out

3

1 2 3

1

 

【数据范围】

编号

1-5

6-10

k

<=10

<=100

 

现场考这个题的时候,我看到这个题就笑了(原来做过类似的(BJOI2010元素)),本弱就是靠这个题进了省队啊。

还是简单的说一下吧

本题就是选一个最大的集合,使该集合的所有子集异或起来都不得0。用拟阵证明这个题用贪心算法是对的,然后用类似高斯消元解异或方程的的东西就解决了。

 

#include<iostream>#include<cstring>#include<algorithm>#include<cstdio>using namespace std;typedef long long LLint;const LLint MAXN=220;LLint Data[MAXN],n,Ans[MAXN],Cnt,Sum,WDY;LLint Matrix[MAXN][MAXN];void Solve(){    memset(Matrix,0,sizeof(Matrix));    LLint N=0,M=0;    for(LLint i=1;i<=n;++i){        LLint x=Data[n-i+1],Ret=0;        while(x){            Matrix[i][++Ret]=x%2;            x/=2;        }        M=max(M,Ret);    }    N=n;    for(LLint i=1,j=1;i<=N&&j<=M;++i,++j){        LLint Flag=0,k=j;        while(k<=M){            if(Matrix[i][k])            {                Flag=1;                break;            }            ++k;        }        if(!Flag){            j--;            continue;        }        Sum+=Data[n-i+1];        for(LLint Pos=1;Pos<=N;++Pos)            swap(Matrix[Pos][j],Matrix[Pos][k]);        for(LLint w=i+1;w<=N;++w){            if(Matrix[w][j]){                for(LLint z=j;z<=M;++z)                    Matrix[w][z]^=Matrix[i][z];            }        }    }}int main(){    freopen("nim.in","r",stdin);    freopen("nim.out","w",stdout);    cin>>n;    for(LLint i=1;i<=n;++i){        cin>>Data[i];        WDY+=Data[i];    }    sort(Data+1,Data+1+n);    Solve();    cout<<WDY-Sum;    //fclose(stdin);    //fclose(stdout);}

 

转载于:https://www.cnblogs.com/Return-0/archive/2013/04/01/2994242.html

最新回复(0)