HDU 1847 Good Luck in CET4 Everybody!

Good Luck in CET-4 Everybody!

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

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能有好的状态。
Good luck in CET-4 everybody!
 
Input
输入数据包含多个测试用例,每个测试用例占一行,包含一个整数n(1<=n<=1000)。
 
Output
如果Kiki能赢的话,请输出“Kiki”,否则请输出“Cici”,每个实例的输出占一行。
 
Sample Input
1 3
 
Sample Output
Kiki Cici
 
Author
lcy
 
Source
 
Recommend
 
题意:博弈的SG问题求解是通法,可是还没有学会,推了一下,猜想一下居然过了。
 
1 win
2 win
3 lost
4 win
5 win
6 lost
7 win
8 win
9 lost
10 win
11 win
12 lost
13 win
14 win
15 lost
16 win
17 win
18 lost
把lost的找出来3 6 9 12 15 18额额,3 的倍数。
 
由于数字的增长是有规律的,1 2 4 8 16 32.....所以,这样的结论也是可以的。一直想找一个特殊情况,打破的这样的3倍数的情况,先试一试。。。。
居然过了
 
 
代码:
 
 
#include<stdio.h>
int main()
{
    int k,n;
    while(scanf("%d",&n)>0)
    {
        k=n%3;
        if(k==0)
            printf("Cici\n");
        else printf("Kiki\n");
    }
    return 0;
}

SG博弈求解的王道。

关键是对SG的求解,直接吧所有的情况打表。sg的值为0的时候,奇异状态,输,否则赢。

给出几个例子说明。贴别人牛人的博客。

Ø g(0)={},G(0)={0, 1, …},f(0)=0;

Ø g(1)={},G(1)={0, 1, …},f(1)=0;

Ø g(2)={#(0)}={f(0)}={0},G(2)={1, 2, …},f(2)=1;

Ø g(3)={#(1)}={f(1)}={0},G(2)={1, 2, …},f(3)=1;

Ø g(4)={#(2), #(1, 1)}={f(2), f(1)+f(1)}={1, 0},G(4)={2, 3, …},f(4)=2;

Ø g(5)={#(3), #(1, 2)}={f(3), f(1)+f(2)}={1, 1},G(5)={0, 2, 3, …},f(5)=0;

Ø g(6)={#(4), #(1, 4), #(2, 2)}={2, 1, 0},G(6)={3, 4, …},f(6)=3;

Ø g(7)={#(4), #(1, 4), #(2, 3)}={2, 2, 0},G(7)={1, 3, 4, …},f(7)=1;

G 图2所示的局面S=(7, 3, 3),有#S=f(7)+f(3)+f(3)=1+1+1=1,故S胜。

G 游戏C的初始局面S=(3, 4, 6),有#S=1+2+3=01+10+11=0,故S负。

 

步骤:

 

2 此类搏弈游戏的一般性解法:

 

F 用一个n元组(a1, a2, …, an),来描述游戏过程中的一个局面。

 

F 用符号#S,表示局面S所对应的二进制数。

 

F 用符号$(x),表示局面(x)的下一步所有可能出现的局面的集合。

 

F 定义集合g(x):设$(x)={S1, S2, …, Sk},则g(x)={#S1, #S2, …, #Sk}。

 

F 令非负整数集为全集,集合G(x)表示集合g(x)的补集。

 

F 定义函数f(n):f(n)=min{G(n)},即f(n)等于集合G(n)中的最小数。

 

F 设局面S=(a1, a2, …, an),#S=f(a1)+f(a2)+…+f(an),采用二进制数的加法。

 

感觉有些繁琐,结合代码分析一下

{

1.首先我们要求出下一个局面可能出现的情况,

2.对应的SG值进行用数组标记.

3.找出最小的值。
}

 

代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int hash[1001],SG[1001];
int arry[12]={12,1,2,4,8,16,32,64,128,256,512,1024};
void getsg()
{
    int tmp,i,j;
    for(i=0;i<=1000;i++)
    {
        memset(hash,0,sizeof(hash[0]));//对每一个sg[i]的值,都要用到
        for(j=1;j<arry[0];j++)
        {
            if(arry[j]>i) //判断是否是下一个局面
                break;
            tmp=i-arry[j];
            hash[SG[tmp]]=1; //对应的g(5)={#(3), #(1, 2)}={f(3),f(1)+f(2)}={1, 1},f(5)=0;
                                                  //f(n)就是SG[i].
        }
        for(j=0;;j++) // 找出最小的。
            if(hash[j]==0)
            {
                SG[i]=j; //此次不能认为可以是SG[i]=1;因为上面hash[SG[tmp]]=1要用到。
                break;
            }
    }
}        
int main()
{
    int k,n;
    memset(SG,0,sizeof(SG[0]));
    getsg();
    while(scanf("%d",&n)>0)
    {
        k=SG[n];
        if(k==0)
            printf("Cici\n");
        else printf("Kiki\n");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/tom987690183/p/3106567.html