博弈论

SG函数

SG函数的定义

先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

SG函数的和

游戏的和的SG函数值等于他包含的各个子游戏SG函数值的异或和

定理

游戏的某个局面必胜,当且仅当该局面对应节点的SG函数值大于0
游戏的某个局面必败,当且仅当该局面对应节点的SG函数值等于0

get_GS

//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
//SG[]:0~n的SG函数值
//S[]:为x后继状态的集合
int f[N],SG[MAXN],S[MAXN];
void  getSG(int n){
   
    int i,j;
    memset(SG,0,sizeof(SG));
    //因为SG[0]始终等于0,所以i从1开始
    for(i = 1; i <= n; i++){
   
        //每一次都要将上一状态 的 后继集合 重置
        memset(S,0,sizeof(S));
        for(j = 0; f[j] <= i && j <= N; j++)
            S[SG[i-f[j]]] = 1;  //将后继状态的SG函数值进行标记
        for(j = 0;; j++) if(!S[j]){
      //查询当前后继状态SG值中最小的非零值
            SG[i] = j;
            break;
        }
    }
}

例题

Fibonacci again and again

Problem Description任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的:
F(1)=1;
F(2)=2;
F(n)=F(n-1)+F(n-2)(n>=3);
所以,1,2,3,5,8,13……就是菲波那契数列。
在HDOJ上有不少相关的题目,比如1005 Fibonacci again就是曾经的浙江省赛题。
今天,又一个关于Fibonacci的题目出现了,它是一个小游戏,定义如下:
1、 这是一个二人游戏;
2、 一共有3堆石子,数量分别是m, n, p个;
3、 两人轮流走;
4、 每走一步可以选择任意一堆石子,然后取走f个;
5、 f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量);
6、 最先取光所有石子的人为胜者;

假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。

Input
输入数据包含多个测试用例,每个测试用例占一行,包含3个整数m,n,p(1<=m,n,p<=1000)。
m=n=p=0则表示输入结束。

Output
如果先手的人能赢,请输出“Fibo”,否则请输出“Nacci”,每个实例的输出占一行。

Sample Input

1 1 1
1 4 1
0 0 0

Sample Output

Fibo
Nacci 

标程

#include <stdio.h>
#include <string.h>
#define MAXN 1000 + 10
#define N 20
int f[N],SG[MAXN],S[MAXN];
void getSG(int n){
   
    int i,j;
    memset(SG,0,sizeof(SG));
    for(i = 1; i <= n; i++){
   
     memset(S,0,sizeof(S));
        for(j = 0; f[j] <= i && j <= N; j++)
        S[SG[i-f[j]]] = 1;
        for(j = 0;;j++) if(!S[j]){
   
        SG[i] = j;
        break;
        }
     }
 }
int main()
{
   
    int n,m,k;
    f[0] = f[1] = 1;
    for(int i = 2; i <= 16; i++)
        f[i] = f[i-1] + f[i-2];
    getSG(1000);     
    while(scanf("%d%d%d",&m,&n,&k),m||n||k)
    {
   
    if(SG[n]^SG[m]^SG[k]) printf("Fibo\n");//问题的和=子问题SG函数异或和 
    else printf("Nacci\n");
     }
    return 0;
}

Good Luck in CET-4 Everybody!

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 18791 Accepted Submission(s): 11771

Problem Description
大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此。当然,作为在考场浸润了十几载的当代大学生,Kiki和Cici更懂得考前的放松,所谓“张弛有道”就是这个意思。这不,Kiki和Cici在每天晚上休息之前都要玩一会儿扑克牌以放松神经。
“升级”?“双扣”?“红五”?还是“斗地主”?
当然都不是!那多俗啊~
作为计算机学院的学生,Kiki和Cici打牌的时候可没忘记专业,她们打牌的规则是这样的:
1、 总共n张牌;
2、 双方轮流抓牌;
3、 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?
当然,打牌无论谁赢都问题不大,重要的是马上到来的CET-4能有好的状态。

Input
输入数据包含多个测试用例,每个测试用例占一行,包含一个整数n(1<=n<=1000)。

Output
如果Kiki能赢的话,请输出“Kiki”,否则请输出“Cici”,每个实例的输出占一行。

Sample Input

1
3

Sample Output

Kiki
Cici

Author
lcy

#include <stdio.h>
#include <string.h>
#define MAXN 1000 + 10
#define N 200
int f[N],SG[MAXN],S[MAXN];
void getSG(int n){
   
    int i,j;
    memset(SG,0,sizeof(SG));
    for(i = 1; i <= n; i++){
   
     memset(S,0,sizeof(S));
        for(j = 0; f[j] <= i && j <= N; j++)
        S[SG[i-f[j]]] = 1;
        for(j = 0;;j++) if(!S[j]){
   
        SG[i] = j;
        break;
        }
     }
 }
int main()
{
   
    int n;
    f[0] =1;
    for(int i = 1; i <= 16; i++)
        f[i] = f[i-1]*2;
    getSG(1005);     
    while(scanf("%d",&n)!=EOF)
    {
   
    if(SG[n]) printf("Kiki\n");
    else printf("Cici\n");
    }
    return 0;
}

Stone Game II hoj4388

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 785 Accepted Submission(s): 464

Problem Description   
Stone Game II comes. It needs two players to play this game. There are some piles of stones on the desk at the beginning. Two players move the stones in turn. At each step of the game the player should do the following operations.
First, choose a pile of stones. (We assume that the number of stones in this pile is n)
Second, take some stones from this pile. Assume the number of stones left in this pile is k. The player must ensure that 0 < k < n and (k XOR n) < n, otherwise he loses.
At last, add a new pile of size (k XOR n). Now the player can add a pile of size ((2*k) XOR n) instead of (k XOR n) (However, there is only one opportunity for each player in each game).
The first player who can’t do these operations loses. Suppose two players will do their best in the game, you are asked to write a program to determine who will win the game.

Input
  
The first line contains the number T of test cases (T<=150). The first line of each test cases contains an integer number n (n<=50), denoting the number of piles. The following n integers describe the number of stones in each pile at the beginning of the game.
You can assume that all the number of stones in each pile will not exceed 100,000.

Output   
For each test case, print the case number and the answer. if the first player will win the game print “Yes”(quotes for clarity) in a single line, otherwise print “No”(quotes for clarity).

Sample Input

3
2
1 2
3
1 2 3
4
1 2 3 3 


Sample Output

Case 1: No
Case 2: Yes
Case 3: No

题目大意:
给出n堆物品,每堆物品都有若干件,现在A和B进行游戏,每人每轮操作一次,按照如下规则:

  1. 任意选择一个堆,假设该堆有x个物品,从中选择k个,要保证0<k<x且0<(x^ k)<x。
  2. 再增加一个大小为x^ k的堆(也就相当于将一个x个物品的堆变成一个k个物品的堆和一个x^ k个物品的堆),另外有一个技能,可以将这个大小为x^ k的堆变成(2*k)^x的堆,但是这个技能每个人只有一次机会可以使用。
  3. 现在问两人轮流操作,都采取最优策略,最后不能操作的人输,问谁会赢。

解题思路
把每堆原来a个物品分成两堆,k和k^ a,证明得,这样分堆,不会影响二进制中1的总数的奇偶性,(后面会给出证明)
为什么要考虑二进制呢?注意题目中的条件,x^ k<x,所以当x的二进制表示中只有一个1时就不能再分了。所以终止条件也有了。对于操作,因为是乘二,所以并不影响奇偶性结果。
设一堆数目中二进制表示1的个数是m,则所有要操作的步骤就是所有的(m-1)加起来。讨论这个总数的奇偶性就行了。


如何统计二进制中1的个数?可以对2不断取模进行,也可以使用n&(n-1)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<map>
using namespace std;
int m;
int main(){
   
    int T;
    scanf("%d",&T);
    for(int j=1;j<=T;j++){
   
        scanf("%d",&m);
        int n=0,x;
        for(int i=
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值