杭电acm 2176 取(m堆)石子游戏 (Nim游戏)

取(m堆)石子游戏

                                Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
                                                     Total Submission(s): 4524    Accepted Submission(s): 2715

Problem Description

m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆

5,7,8,9,10先取者胜,先取者第1次取时可以从有8个的那一堆取走7个剩下1个,也可以从有9个的中那一堆取走9个剩下0个,也可以从有10个的中那一堆取走7个剩下3个.

Input

输入有多组.每组第1行是m,m<=200000. 后面m个非零正整数.m=0退出.

Output

先取者负输出No.先取者胜输出Yes,然后输出先取者第1次取子的所有方法.如果从有a个石子的堆中取若干个后剩下b个后会胜就输出a b.参看Sample Output.

Sample Input

2

45 45

3

3 6 9

5

5 7 8 9 10

0

Sample Output

No

Yes

9 5

Yes

8 1

9 0

10 3

这道题是一道典型的Nim游戏题。对于两个人取石子,定义P-position和N-position,其中P代表Previous,N代表Next。直观的说,上一次move的人有必胜策略的局面是P-position,就是说对于这一轮,后出手的可以保证必胜也就是先出手的一定会失败。现在轮到move的人有必胜策略的局面是N-position,也就是对于这一局,现在轮到的这一局,先手可以保证必胜。对上述内容可以进行以下定义:

1.无法进行任何移动的局面(也就是terminal position)是P-position;

2.可以移动到P-position的局面是N-position;

3.所有移动都导致N-position的局面是P-position(这样想,N-position是先手必胜,P-position是后手必胜,现在的情况可以推出下一move是先手胜,下一轮的先手对于本轮是后手);

好了,现在知道这三点就好写题了,下面是关于这三点推论:对于局面aN(a1,a2,……,an)

1.所有的terminal position都是P-position,显然temerminal position的可能性只有一个,就是全0,也就是a1^a2^……^an=0;

2.对于aN存在一个合法的移动使得a1^a2^……^an=0;我们来设未移动时a1^a2^……^an=k;则一定存在某个ai,它的二进制表示在k的最高位上是1。这时ai^k<ai一定成立。这时将ai=ai^k,此时a1^a2^……^an=0.

3.因为异或满足消去率,所以当a1^a2^……^an=0时,一定不存在某个合法移动使得a1^a2^……^ai‘^……^an=0;

#include<iostream>
using namespace std;
const int maxn=200005;
int num[maxn];
int main(){
    int m,r;
    while(~scanf("%d",&m)&&m){
        r=0;
        for(int i=0;i<m;i++){
            scanf("%d",&num[i]);
            r=r^num[i];
        }
        if(r==0)printf("No\n");
        else{
            printf("Yes\n");
            for(int i=0;i<m;i++){
                if(num[i]>(num[i]^r))
                printf("%d %d\n",num[i],num[i]^r);
            }
        }
    }
} 

原文地址:https://www.cnblogs.com/fromzore/p/9839442.html

时间: 2024-11-07 19:26:23

杭电acm 2176 取(m堆)石子游戏 (Nim游戏)的相关文章

杭电 2176 取(m堆)石子游戏(博弈)

取(m堆)石子游戏 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1741    Accepted Submission(s): 1014 Problem Description m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,10先取者胜,先取者第1次

HDU 2176 取(m堆)石子游戏 博弈

取(m堆)石子游戏 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3598    Accepted Submission(s): 2151 Problem Description m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,10先取者胜,先取者第1次取

HDU 2176 取(m堆)石子游戏 (Nim博弈)

取(m堆)石子游戏 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1937    Accepted Submission(s): 1115 Problem Description m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,10先取者胜,先取者第1次取

HDU 2176 取(m堆)石子游戏 (尼姆博奕)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2176 m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,10先取者胜,先取者第1次取时可以从有8个的那一堆取走7个剩下1个,也可以从有9个的中那一堆取走9个剩下0个,也可以从有10个的中那一堆取走7个剩下3个. Input输入有多组.每组第1行是m,m<=200000. 后面m个非零正整数.m=0退出. Output先取

hdu 2176 取(m堆)石子游戏 (裸Nim)

题意: m堆石头,每堆石头个数:a[1]....a[m]. 每次只能在一堆里取,至少取一个. 最后没石子取者负. 先取者负输出NO,先取胜胜输出YES,然后输出先取者第1次取子的所有方法.如果从有a个石子的堆中取若干个后剩下b个后会胜就输出a b 思路: 裸的NIM. 单看一堆石子,没有石头sg[0]=0,一个石头sg[1]=1,....n个石头sg[n]=n. 故SG[a[1],a[2]...a[m]] = sg[a[1]]^...^sg[a[m]] = a[1]^...^a[m] SG=0

HDU 2176 取(m堆)石子游戏 &amp;&amp; HDU1850 Being a Good Boy in Spring Festivaly

HDU2176题意: m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子. 通过 SG定理 我们可以知道每一个数的SG值,等于这个数到达不了的前面数中的最小值.本题题意和尼姆博弈一样,即可以在一堆中任意个石子,所以也就是说每个数都可以到达前面经过的每一个数,所以每一个数的SG值就是它本身.又因为有好多堆石子,所以可以看作多个一堆石子的游戏,我们可以让n代表每一堆石子的数量,那么让所有堆的SG(n)相互异或得到的结果就是答案(这里只是用SG定义来

HDU 2176 取(m堆)石子游戏 尼姆博弈

题目思路: 对于尼姆博弈我们知道:op=a[1]^a[2]--a[n],若op==0先手必败 一个简单的数学公式:若op=a^b 那么:op^b=a: 对于第i堆a[i],op^a[i]的值代表其余各个堆值的亦或值. 我们现在希望将a[i]改变成某个更小的值使得,op^a[i]=0,反过来a[i]=op^0,输出它就好了 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<math.h&

HDU 2176 取(m堆)石子游戏

nim基础博弈 #include<stdio.h> #include<iostream> #include<cstring> #include<queue> using namespace std; int main() { int n,a[200010]; while(~scanf("%d",&n) && n) { int ans = 0; for(int i = 0;i < n;i++) { scanf(

hdu 5088 高斯消元n堆石子取k堆石子使剩余异或值为0

http://acm.hdu.edu.cn/showproblem.php?pid=5088 求能否去掉几堆石子使得nim游戏胜利 我们可以把题目转化成求n堆石子中的k堆石子数异或为0的情况数.使用x1---xn表示最终第i堆石子到底取不取(1取,0不取),将每堆石子数画成2进制的形式,列成31个方程来求自由变元数,最后由于自由变元能取1.0两种状态,所以自由变元数多于0即可输出Yes. 注意有40+个方程,因为A[I]<=1e12.... #include <cstdio> #incl