9.30 CCPC女队赛拯救计划(倒计时17天)_博弈论

总结一下

我需要永远记住这种弱小的感觉。
看了一手北航的考研真题,第一题就是“求kmp失效函数”。
老实说看题目都是熟悉的内容,但真要写又是另外一码事。
先以清华大学作为目标,看能不能冲一手北航。

JAVA

立个小flag,国庆期间完成java期末课设
我已经没有机会休息了。 ‘
完蛋了,我也要变成功利B了。

  1. String args[]和String[]args
    String args[]单从类型上来讲属于字符串类型, 而从变量本身来讲是一个数组类型, 因此组合起来说明此变量为一个字符串类型的数组, 也就是说数组中的所有元素都为String类型.
    String[] args单从类型上来讲属于字符串数组类型, 而从变量本身来讲就是一个单纯的引用变量, 因此这种方式可以更明显地体现出是否为数组这一点. 因为从类型上就可以直接看出变量为一数组类型引用。

String args[], argt;
//args是字符串数组类型, 而argt则就是字符串变量
String[] args,argt;
//args与argt都是字符串数组类型.

  1. 浮点数的比较
    不能用==或者equal()

浮点数采用符号+阶码+尾数进行表示。
在Java中,单精度浮点数float类型占32位,它的二进制表示方式为:

  • 符号位:1位,0表示正数; 1表示负数
  • 指数位:8位,用来表示指数(要加上偏移量)
  • 小数位:23位,用来表示小数
    实际上计算机中的浮点数表示方法和科学技术法类似,小数的位数决定了浮点数的精度。
    当一个十进制的小数转换成二进制时,很有可能无法使用这么多小数位表示它。
    因此使用浮点数的时候,实际存储的尾数是被截取或者舍入之后的值。因此使用浮点数进行计算的时候就不得不考虑精度问题,即浮点数无法精确计算。

不能用==

// 对f1执行11次加0.1的操作
float f1 = 0.0f;
for (int i = 0; i < 11; i++) {
    f1 += 0.1f;
}
// f2是0.1*11的结果
float f2 = 0.1f * 11;
System.out.println(f1 == f2);   // 结果为false
//f1 = 1.1000001
//f2 = 1.1

可以定制允许误差范围
尽管无法做到精确比较,但是我们可以确定一个误差范围,当小于这个误差范围的时候就认为这两个浮点数相等。例如:

final float THRESHOLD = 0.000001;   
// 设置最大误差不超过0.000001
float f1 = 0.0f;
for (int i = 0; i < 11; i++) {
    f1 += 0.1f;
}
float f2 = 0.1f * 11;
if (Math.abs(f1 - f2) < THRESHOLD) {
    System.out.println("f1 equals f2");
}

可以使用BigDecimal
BigDecimal是一个不可变的、能够表示大的十进制整数的对象。
注意使用BigDecimal时,要使用参数为String的构造方法,而不要使用参数为double的构造方法,防止产生精度丢失。
使用BigDecimal进行运算,使用它的compareTo()方法比较即可。

private void compareByBigDecimal() {
    BigDecimal f1 = new BigDecimal("0.0");
    BigDecimal pointOne = new BigDecimal("0.1");
    for (int i = 0; i < 11; i++) {
        f1 = f1.add(pointOne);
    }

    BigDecimal f2 = new BigDecimal("0.1");
    BigDecimal eleven = new BigDecimal("11");
    f2 = f2.multiply(eleven);

    System.out.println("f1 = " + f1);
    System.out.println("f2 = " + f2);

    if (f1.compareTo(f2) == 0) {
        System.out.println("f1 and f2 are equal using BigDecimal");
    } else {
        System.out.println("f1 and f2 are not equal using BigDecimal");
    }
}

博弈论

一、巴什博弈

  1. 问题模型:只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个,最后取光者得胜。
  2. 解决思路:
    当n=(m+1)*r+s,(r为任意自然数,s≤m)时,
    如果先取者要拿走s个物品,如果后取者拿走x(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。
    总之,要保持给对手留下(m+1)的倍数,就能最后获胜。
    说到底,从最终状态反推。
  3. 变形:条件不变,改为最后取光的人输。
    当n=m+1时,由于一次最多只能取m个,所以无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜.所以当一方面对的局势是n%(m+1)=0时,其面临的是必败的局势。
    结论:当n%(m+1)==0时后手胜利。
  4. 题目练习:HDOJ:2188 2149 1846

HDU 2188

对于四川同胞遭受的灾难,全国人民纷纷伸出援助之手,几乎每个省市都派出了大量的救援人员,这其中包括抢险救灾的武警部队,治疗和防疫的医护人员,以及进行心理疏导的心理学专家。根据要求,我校也有一个奔赴灾区救灾的名额,由于广大师生报名踊跃,学校不得不进行选拔来决定最后的人选。经过多轮的考核,形势逐渐明朗,最后的名额将在“林队”和“徐队”之间产生。但是很巧合,2个人的简历几乎一模一样,这让主持选拔的8600很是为难。无奈,他决定通过捐款来决定两人谁能入选。
选拔规则如下:
1、最初的捐款箱是空的;
2、两人轮流捐款,每次捐款额必须为正整数,并且每人每次捐款最多不超过m元(1<=m<=10)。
3、最先使得总捐款额达到或者超过n元(0<n<10000)的一方为胜者,则其可以亲赴灾区服务。
我们知道,两人都很想入选志愿者名单,并且都是非常聪明的人,假设林队先捐,请你判断谁能入选最后的名单?
Input
输入数据首先包含一个正整数C,表示包含C组测试用例,然后是C行数据,每行包含两个正整数n,m,n和m的含义参见上面提到的规则。
Output
对于每组测试数据,如果林队能入选,请输出字符串"Grass", 如果徐队能入选,请输出字符串"Rabbit",每个实例的输出占一行。
Sample Input
2
8 10
11 10
Sample Output
Grass
Rabbit

题意分析
两人轮流捐款,每次捐款额必须为正整数,并且每人每次捐款最多不超过m元(1<=m<=10)。最先使得总捐款额达到或者超过n元(0<n<10000)的一方为胜者,则其可以亲赴灾区服务。

#include <iostream>
#include <cstdio>
#include <string>
#include <string.h>
#include <map>
#include <vector>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cmath>
#include <queue>
#include <set>
using namespace std;
int main()
{
    int c;
    int n, m;
    cin >> c;
    while(c--)
    {
        cin >> n >>m;
        if(n%(m+1)!=0)
        printf("Grass\n");
        else
        printf("Rabbit\n");
    }
    return 0;
}

HDU 2194

Problem Description
虽然不想,但是现实总归是现实,Lele始终没有逃过退学的命运,因为他没有拿到奖学金。现在等待他的,就是像FarmJohn一样的农田生涯。
要种田得有田才行,Lele听说街上正在举行一场别开生面的拍卖会,拍卖的物品正好就是一块20亩的田地。于是,Lele带上他的全部积蓄,冲往拍卖会。
后来发现,整个拍卖会只有Lele和他的死对头Yueyue。
通过打听,Lele知道这场拍卖的规则是这样的:刚开始底价为0,两个人轮流开始加价,不过每次加价的幅度要在1~N之间,当价格大于或等于田地的成本价 M 时,主办方就把这块田地卖给这次叫价的人。
Lele和Yueyue虽然考试不行,但是对拍卖却十分精通,而且他们两个人都十分想得到这块田地。所以他们每次都是选对自己最有利的方式进行加价。
由于Lele字典序比Yueyue靠前,所以每次都是由Lele先开始加价,请问,第一次加价的时候,
Lele要出多少才能保证自己买得到这块地呢?
Input
本题目包含多组测试,请处理到文件结束(EOF)。每组测试占一行。
每组测试包含两个整数M和N(含义见题目描述,0<N,M<1100)
Output
对于每组数据,在一行里按递增的顺序输出Lele第一次可以加的价。两个数据之间用空格隔开。
如果Lele在第一次无论如何出价都无法买到这块土地,就输出"none"。
Sample Input
4 2
3 2
3 5
Sample Output
1
none
3 4 5

题意分析
套公式。
我受不了了,一直PE。
想着是不是空格的问题把后导空格改成前导空格也还是不行。真是奇怪。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <cstring>
#include <iomanip>

#define ll long long
using namespace std;
const int MAX = 1e4 + 10;
const int INF = 1e9 + 7;

int main() {

    int n, m, i;
    while (scanf("%d%d",&n,&m)!=EOF) {
        if (n <= m) {
        //必胜情况1
            for (i = n; i <= m; i++) {
                if (i != n) printf(" %d",i);
                else printf("%d",i);
            }
            printf("\n");
        } else if (n % (m + 1))
        //必胜情况2
        {
            for (i = 1; i <= m; i++) {
                if ((n - i) % (m + 1) == 0)
                //必须留下m+1的倍数
                {
                    if (i == 1)  printf("%d",i);
                    else  printf(" %d",i);
                }
            }
            printf("\n");
        } else  printf("none\n");


    }
}

HDU1846

Problem Description
十年前读大学的时候,中国每年都要从国外引进一些电影大片,其中有一部电影就叫《勇敢者的游戏》(英文名称:Zathura),一直到现在,我依然对于电影中的部分电脑特技印象深刻。
今天,大家选择上机考试,就是一种勇敢(brave)的选择;这个短学期,我们讲的是博弈(game)专题;所以,大家现在玩的也是“勇敢者的游戏”,这也是我命名这个题目的原因。
当然,除了“勇敢”,我还希望看到“诚信”,无论考试成绩如何,希望看到的都是一个真实的结果,我也相信大家一定能做到的~
各位勇敢者要玩的第一个游戏是什么呢?很简单,它是这样定义的:
1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个;
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子;
5、 最先取光石子的一方为胜;
如果游戏的双方使用的都是最优策略,请输出哪个人能赢。
Input
输入数据首先包含一个正整数C(C<=100),表示有C组测试数据。
每组测试数据占一行,包含两个整数n和m(1<=n,m<=1000),n和m的含义见题目描述。
Output
如果先走的人能赢,请输出“first”,否则请输出“second”,每个实例的输出占一行。
Sample Input
2
23 2
4 3
Sample Output
first
second

题意分析
直接带公式,除非n%(m+1)==0,不然先手必胜!

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <cstring>
#include <iomanip>

#define ll long long
using namespace std;
const int MAX = 1e4 + 10;
const int INF = 1e9 + 7;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

int T;
    cin>>T;
    while(T--) {
        int n, m;
        cin >> n >> m;
        if (m >= n)
            cout << "first\n";
        else {
            if (n % (m + 1) == 0)
                cout << "second\n";
            else  cout << "first\n";
        }
    }
}

二、威佐夫博弈

  1. 问题模型:有两堆各若干个物品,两个人轮流从任一堆取至少一个或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
  2. 解决思路:
    百度百科解释
    找到奇异局势。
    这个博客讲的很清楚
    在这里插入图片描述

a[k] = (int) ((b[k] - a[k])*1.618)
注:这里的int是强制类型转换,不是四舍五入。
有些题目要求精度较高,我们可以用下述式子来表示这个值:
1.618 = (sqrt(5.0) + 1) / 2

  1. 满足上公式的局势性质:
    (1)任何自然数都包含在一个且仅有一个奇异局势中。
    由于ak是未在前面出现过的最小自然数,所以有ak > ak-1 ,而 bk= ak + k > ak-1 + k-1 = bk-1 > ak-1 。所以性质成立。
    (2)任意操作都可将奇异局势变为非奇异局势。
    若只改变奇异局势(ak,bk)的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使(ak,bk)的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
    (3)采用适当的方法,可以将非奇异局势变为奇异局势。
    假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了奇异局势(0,0);如果a = ak ,b > bk,那么,取走b – bk个物体,即变 为奇异局势;如果 a = ak , b < bk ,则同时从两堆中拿走 ak – ab – ak个物体,变为奇异局势( ab – ak , ab – ak+ b – ak);如果a > ak ,b= ak + k,则从第一堆中拿走多余的数量a – ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k),从第二堆里面拿走 b – bj 即可; 第 二种,a=bj (j < k),从第二堆里面拿走 b – aj 即可。

  2. 结论:两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。

POJ 1067

取石子游戏
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 49887 Accepted: 17094
Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。
Output
输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。
Sample Input
2 1
8 4
4 7
Sample Output
0
1
0

POJ 1067

模版题

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <cstring>
#include <iomanip>

#define ll long long
using namespace std;
const int MAX = 1e4 + 10;
const int INF = 1e9 + 7;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int a, b;
    while (cin>>a>>b) {
     if(a>b) swap(a,b);
        double r=(sqrt(5.0) + 1.0) / 2;
        int temp=int(1*(b-a)*r);
       // cout<<"temp="<<temp<<endl;
        if(temp==a)
            cout<<1<<endl;
        else cout<<0<<endl;
    }

}

三、Fibonacci博弈

  1. 问题模型:
    有一堆个数为n的石子,游戏双方轮流取石子,满足:
    (1)先手不能在第一次把所有的石子取完;
    (2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。 约定取走最后一个石子的人为赢家。

  2. 解决思路:
    当n为Fibonacci数时,先手必败。即存在先手的必败态当且仅当石头个数为Fibonacci数。先手胜当且仅当n不是Fibonacci数。换句话说,必败态构成Fibonacci数列
    证明:根据“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。如n=83 = 55+21+5+2,我们看看这个分解有什么指导意义:假如先手取2颗,那么后手无法取5颗或更多,而5是一个Fibonacci数,那么一定是先手取走这5颗石子中的最后一颗,同理,接下去先手取走接下来的后21颗中的最后一颗,再取走后55颗中的最后一颗,那么先手赢。
    反证:如果n是Fibonacci数,如n=89:记先手一开始所取的石子数为y
    (1)若y>=34颗(也就是89的向前两项),那么一定后手赢,因为89-34=55=34+21<2*34。
    (2)y<34时剩下的石子数x介于55到89之间,它一定不是一个Fibonacci数,把x分解成Fibonacci数:x=55+f[i]+…+f[j],若,如果f[j]<=2y,那么对B就是面临x局面的先手,所以根据之前的分析,后手只要先取f[j]个即可,以后再按之前的分析就可保证必胜。

HDU2516

取石子游戏
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 11398 Accepted Submission(s): 6941
Problem Description
1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".
Input
输入有多组.每组第1行是2<=n<2^31. n=0退出.
Output
先取者负输出"Second win". 先取者胜输出"First win".
参看Sample Output.
Sample Input
2
13
10000
0
Sample Output
Second win
Second win
First win

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <cstring>
#include <iomanip>

#define ll long long
using namespace std;
const int MAX = 1e4 + 10;
const int INF = 1e9 + 7;
int Fib[51];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    Fib[0] = 1;
    Fib[1] = 2;
    for (int i = 2; i < 45; i++)
        Fib[i] = Fib[i - 1] + Fib[i - 2];
    //打一手斐波那契表
    ll n;
    while (cin >> n && n != 0) {
        bool flag=0;
        for (int i = 0; i < 45; i++)
            if (Fib[i] == n) {
                cout << "Second win" << endl;
                flag=1;
                break;
            }
        if(!flag) cout << "First win" << endl;

    }
}


四、尼姆博弈

  1. 问题模型:
    有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
  2. 解决思路:
    用(a,b,c)表示某种局势,显证(0,0,0)是第一种奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。
    搞定这个问题需要把必败态的规律找出:
    (a,b,c)是必败态等价于a^b^c=0(^表示异或运算)
  3. 证明:(1)任何p(a,b,c)=0的局面出发的任意局面(a,b,c’);一定有p(a,b,c’)不等于0。否则可以得到c=c’。(2)任何p(a,b,c)不等于0的局面都可以走向 p(a,b,c)=0的局面 (3)对于 (4,9,13) 这个容易验证是奇异局势
    其中有两个8,两个4,两个1,非零项成对出现,这就是尼姆和为零的本质。别人要是拿掉13里的8或者1,那你就拿掉对应的9中的那个8或者1;别人要是拿掉13里的4,你就拿掉4里的4;别人如果拿掉13里的3,就把10作分解,然后想办法满足非零项成对即可。
  4. 推广一:如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?假设 a < b< c,我们只要将 c 变为 a^b,即可,因为有如下的运算结果: a^b^(a^b)=(a^a)^(b^b)=0^0=0。要将c 变为a^b,只从 c中减去 c-(a^b)
  5. 推广二:当石子堆数为n堆时,则推广为当对每堆的数目进行异或之后值为零是必败态。

五、公平组合博弈(Impartial Combinatori Games)

SG 函数!!!

  1. 定义:
    (1)两人参与。
    (2)游戏局面的状态集合是有限。
    (3)对于同一个局面,两个游戏者的可操作集合完全相同
    (4)游戏者轮流进行游戏。
    (5)当无法进行操作时游戏结束,此时不能进行操作的一方算输。
    (6)无论游戏如何进行,总可以在有限步数之内结束。
  2. 模型:
    给定一个有向无环图和一个起始顶点上的一枚棋子,两名选手交替的将这枚棋子沿有向边进行移动,无法移动者判负。事实上,这个游戏可以认为是所有公平组合游戏(Impartial Combinatori Games)的抽象模型。其实,任何一个ICG都可以通过把每个局势看成一个顶点,对每个局势和它的子局势连一条有向边来抽象成这个“有向图游戏”。
  3. 解决思路:
    现在,假定我们给出两个游戏G1 和 G2。如果我们只知道单个游戏的P-状态和N-状态我们能够正确地玩好游戏和G1 + G2吗?答案是否定的。不难看出两个P-状态的和总是P-状态,P-状态和N-状态的和总是N-状态。但是两个N-状态的和既可能是P-状态也可能是N-状态。因此,只知道单个游戏的P-状态和N-状态是不够的。
    为了正确地玩好游戏和我们需要推广P-状态和N-状态,它就是Sprague-Grudy函数(或者简称为sg函数)
  4. Sprague-Grudy定理:
    令N = {0, 1, 2, 3, …} 为自然数的集合。Sprague-Grundy 函数给游戏中的每个状态分配了一个自然数。结点v的Grundy值等于没有在v的后继的Grundy值中出现的最小自然数.
    形式上:给定一个有限子集 S ⊂ N,令mex S(最小排斥值)为没有出现在S中的最小自然数。定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
    对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Garundy函数g如下:g(x)=mex{ g(y) | y是x的后继 }。
  5. 性质:
    (1)所有的终结点所对应的顶点,其SG值为0,因为它的后继集合是空集——所有终结点是必败点(P点)。
    (2)对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0——无论如何操作,从必败点(P点)都只能进入必胜点(N点)//对手走完又只能把N留给我们。
    (3)对于一个g(x)!=0的顶点,必定存在一个后继点y满足g(y)=0——从任何必胜点(N点)操作,至少有一种方法可以进入必败点(P点)//就是那种我们要走的方法。
  6. 应用:
    (1)可选步数为1-m的连续整数,直接取模即可,SG(x) = x % (m+1);
    (2)可选步数为任意步,SG(x) = x;
    (3)可选步数为一系列不连续的数,用mex(计算每个节点的值)

HDU 1847

HDU1847

Good Luck in CET-4 Everybody!
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 18767 Accepted Submission(s): 11758
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

#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;
}

HDU 1536

HDU 3980

待补全…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值