HDU 1847-Good Luck in CET-4 Everybody!(博弈-SG函式/找規律)

kewlgrl發表於2016-05-10

Good Luck in CET-4 Everybody!

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



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
lcy   |   We have carefully selected several similar problems for you:  1848 1849 1846 2147 2149

解題思路:

①n是三的倍數時,是必敗點。

From簡單證明一下:3的倍數是必敗狀態。

  • 如果n % 3 = 1,那麼拿走1個石子;如果n % 3 = 2,那麼拿走兩個石子,都將轉移到3的倍數的狀態。所以每個必勝狀態都有一個後繼是必敗狀態。
  • 如果n % 3 = 0,因為2i裡面沒有一個是3的倍數,所以不管怎麼拿,剩下的石子數n' % 3 != 0.所以每個必敗狀態的所有後繼都是必勝狀態。

②用sg函式做。


AC①

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        if(n%3)printf("Kiki\n");//必敗點
        else printf("Cici\n");

    }
    return 0;
}


AC②


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

#define MAXN 1010
int f[MAXN];
bool sg[MAXN];

void solve()
{
    int i,j,cnt=9;
    f[0]=1;
    for(i=1; i<=9; i++) //1000內的2次冪數
        f[i]=f[i-1]*2;
    sg[0]=false;
    for(i=1; i<=1000; i++) //求sg值,i表示牌的數目
    {
        sg[i]=false;
        for(j=0; j<=cnt; ++j)
            sg[i]|=f[j]<=i&&!sg[i-f[j]];
    }
}
int main()
{
    int n;
    solve();
    while(~scanf("%d",&n))
    {
        if(sg[n]) printf("Kiki\n");//必敗點
        else printf("Cici\n");
    }
    return 0;
}


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1010
int sg[MAXN],f[MAXN],temp[MAXN];

void solve()
{
    int i,j,cnt=9;
    f[0]=1;
    for(i=1; i<=9; i++) //1000內的2次冪數
        f[i]=f[i-1]*2;
    memset(sg,-1,sizeof(sg));
    sg[0]=0;
    for(i=1; i<=1000; i++) //求sg值,i表示牌的數目
    {
        memset(temp,-1,sizeof(temp));
        for(j=0; j<=cnt&&f[j]<=i; ++j)
            temp[sg[i-f[j]]]=0;//i-f[j]表示從i張牌中取走f[j]張後的狀態
        for(j=0;; ++j)//求mem
            if(temp[j]==-1)
            {
                sg[i]=j;
                break;
            }
    }
}
int main()
{
    int n;
    solve();
    while(~scanf("%d",&n))
    {
        if(sg[n]) printf("Kiki\n");//必敗點
        else printf("Cici\n");
    }
    return 0;
}


相關文章