题解报告:hdu 1847 Good Luck in CET-4 Everybody!(入门SG值)
Problem Description
作为计算机学院的学生,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
Output
Sample Input
Sample Output
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
while(cin>>n){
if(n%)cout<<"Kiki"<<endl;//不是3的倍数,先手必赢
else cout<<"Cici"<<endl;//是3的倍数,后手必赢
}
return ;
}
这题还可以用SG值解决,所谓的SG值就是记录当前状态是N是P的具体值,N-position表示必赢状态(其SG值不为0),P-position表示必输状态(其SG值为0)。下面介绍怎么求SG值:首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示不属于mex这个集合的最小非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Grundy函数g如下:g(n)=mex{ g(m) | m是n的后继 },这里的g(n)即sg[n]
拿本题的栗子来讲:首先有sg[0]=0,f[]={1,2,4...};(f数组存放可以抓走扑克牌的张数,并且按升序存放)
当n=1时,先手可以抓走1-f{1}张牌,剩余{0}张,mex{sg[0]}={0},故sg[1]=1;
当n=2时,先手可以抓走2-f{1,2}张牌,剩余{1,0}张,mex{sg[1],sg[0]}={1,0},故sg[2]=2;
当n=3时,先手可以抓走3-f{1,2}张牌,剩余{2,1}张,mex{sg[2],sg[1]}={2,1},故sg[3]=0;
当n=4时,先手可以抓走4-f{1,2,4}张牌,剩余{3,2,0}张,mex{sg[3],sg[2],sg[0]}={0,2,0},故sg[4]=1;
当n=5时,先手可以抓走5-f{1,2,4}张牌,剩余{4,3,1}张,mex{sg[4],sg[3],sg[1]}={1,0,1},故sg[5]=2;
以此类推.....
n 0 1 2 3 4 5 6 7 8 9....
sg[n] 0 1 2 0 1 2 0 1 2 ....
由上述实例我们就可以得到1~n的SG值的计算步骤,如下所示:
①、使用f数组保存可抓取的扑克牌张数。
②、然后使用vis数组来标记当前状态n的后继m状态。
③、最后模拟mex运算,也就是我们在集合mex中查找未被标记值的最小值,将其赋值给sg(n)。
④、不断的重复 ② - ③ 的步骤,即可完成计算1~n的SG值。
关于3种SG值计算方法(重点):
1、可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);
2、可选步数为任意步,SG(x) = x;
3、可选步数为一系列不连续的数,用get_SG()计算
此题就是选取第3种方法来计算SG值。
#include<bits/stdc++.h>
using namespace std;
const int maxn = ;
int n,f[],sg[maxn];
bool vis[maxn];
//f[]:每次抓牌的个数
//sg[]: 0~n的SG函数值
//vis[]:mex{}
void init(){//初始化
f[] = ;//下标从1开始
for(int i=;i<=;++i)f[i]=f[i-]*;//这里只需枚举到512即可,因为1024已经超过n=1000了
}
void get_SG(){
memset(sg,,sizeof(sg));
for(int i=;i<maxn;++i){
memset(vis,false,sizeof(vis));//每轮到当前i就重新初始化vis都为未访问状态,找出不属于这个集合的最小非负整数
for(int j=;j< && f[j]<=i;++j)//j<11要放在判断条件的前面,不然会出现错误即越界,因为数组长度只有10
vis[sg[i-f[j]]]=true;//i-f[j]为后继状态,vis[sg[i-f[j]]]收录mex集合
for(int j=;j<maxn;++j)//求没有出现在mex集合中的非负最小值
if(!vis[j]){sg[i]=j;break;}
}
}
int main()
{
init();
get_SG();
while(cin>>n){
if(sg[n])cout<<"Kiki"<<endl;//当sg[n]不为0时,即为N-position,此时先手必赢
else cout<<"Cici"<<endl;
}
return ;
}
再贴一下dfs版本代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = ;
int n,f[],sg[maxn];
/*
SG值:一个点的SG值就是一个不等于它的后继点的SG的且大于等于零的最小整数。
同mex()函数。简单点来讲就是当前状态离最近一个必败点的距离。距离为0就是必败点
SG(x)=mex(S),S是x的后继状态的SG函数值集合,mex(S)表示不在S内的最小非负整数
SG值是P/N状态的具体化
*/
int mex(int x){//求该点的SG值(采用记忆化搜索)
if(sg[x]!=-)return sg[x];//搜索过了
bool vis[maxn];//vis数组要在此声明,不然会出错,因为这里是递归操作
memset(vis,false,sizeof(vis));
for(int i=;i<=;++i){
int tmp=x-f[i];
if(tmp<)break;//当差值小于0,直接退出
sg[tmp]=mex(tmp);//找sg[tmp]的后继值
vis[sg[tmp]]=true;//回退的时候标记后继sg值标记为true
}
for(int i=;i<=maxn;++i)//每次break退出时就取不属于mex集合的最小非负整数
if(!vis[i]){sg[x]=i;break;}
return sg[x];//返回x的最小非负整数
}
int main()
{
f[]=;
for(int i=;i<=;++i)
f[i]=f[i-]*;//只需枚举到512就行了,因为1024>1000没必要取到
memset(sg,-,sizeof(sg));//初始化为-1,记忆化搜索
while(cin>>n){
if(mex(n))cout<<"Kiki"<<endl;//当sg[n]不为0时即为N-position,先手必赢
else cout<<"Cici"<<endl;
}
return ;
}
更多详解参考一下这篇博文:博弈论 SG函数
题解报告:hdu 1847 Good Luck in CET-4 Everybody!(入门SG值)的更多相关文章
- HDU 1847 Good Luck in CET-4 Everybody! (博弈论sg)
Good Luck in CET-4 Everybody! Problem Description 大学英语四级考试就要来临了,你是不是在紧张的复习?或许紧张得连短学期的ACM都没工夫练习了.反正我知 ...
- HDU.1847 Good Luck in CET-4 Everybody! ( 博弈论 SG分析)
HDU.1847 Good Luck in CET-4 Everybody! ( 博弈论 SG分析) 题意分析 简单的SG分析 题意分析 简单的nim 博弈 博弈论快速入门 代码总览 //#inclu ...
- hdu 1847 Good Luck in CET-4 Everybody!(巴什博弈)
Good Luck in CET-4 Everybody! HDU - 1847 大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Ci ...
- HDU 1847 Good Luck in CET-4 Everybody! (巴什博弈)
题目链接:HDU 1847 Problem Description 大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此. ...
- hdu 1847 Good Luck in CET-4 Everybody!(sg)
Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...
- HDU 1847 Good Luck in CET-4 Everybody!(规律,博弈)
Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...
- HDU 1847 Good Luck in CET-4 Everybody!(找规律,或者简单SG函数)
Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...
- HDU 1847 Good Luck in CET-4 Everybody!
题解:巴什博弈,2^k+1=3N或2^k2=3N,所以3N为P-position,3N+r为N-position. #include <cstdio> int main(){ int n; ...
- HDU 1847 Good Luck in CET-4 Everybody!(找规律版巴什博奕)
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission( ...
随机推荐
- P2746 [USACO5.3]校园网Network of Schools// POJ1236: Network of Schools
P2746 [USACO5.3]校园网Network of Schools// POJ1236: Network of Schools 题目描述 一些学校连入一个电脑网络.那些学校已订立了协议:每个学 ...
- 【codeforces 527B】Error Correct System
[题目链接]:http://codeforces.com/contest/527/problem/B [题意] 给你两个字符串; 允许你交换一个字符串的两个字符一次: 问你这两个字符串最少会有多少个位 ...
- BUPT2017 wintertraining(15) #9
下面不再说明题意了请自行读题,直接放contest链接. https://vjudge.net/contest/151607 A.考虑当火车隔k站一停时 区间长度 >= k 的纪念品一定能买到 ...
- PL/SQL Challenge 每日一题:2014-3-14 11gR2中带RELIES_ON子句的RESULT_CACHE函数
PL/SQL Challenge 每日一题:2014-3-14 11gR2中带RELIES_ON子句的RESULT_CACHE函数 最先答对且答案未经编辑的puber将获得纪念章一枚(答案不可编辑但可 ...
- 清北学堂模拟赛d3t1 a
[问题描述]你是能看到第一题的friends呢.——hja 怎么快速记单词呢?也许把单词分类再记单词是个不错的选择.何大爷给出了一种分单词的方法,何大爷认为两个单词是同一类的当这两个单词的各个字母的个 ...
- PHP array_diff()
定义和用法 array_diff() 函数返回两个数组的差集数组.返回的数组的元素都取自被比较的数组(既第一个数组). 在返回的数组中,键名保持不变. 语法 array_diff(array1,arr ...
- jvm的运行模式 client和 server两种
jvm的运行模式 client和 server两种 学习了:https://www.cnblogs.com/fsjohnhuang/p/4270505.html 在jdk 9的情况下,好像没有clie ...
- VMware Workstation 集群仲裁磁盘和数据共享磁盘的创建
近期项目须要对SQL Server建立集群服务,多个SQL Server数据库建立集群服务,对外提供唯一的URL訪问地址.当主节点断电.断网后,通过心跳线将消息传递到备用节点.备用节点在3秒内接管数据 ...
- Openstack能解决这些问题吗?请各位大侠一起来讨论
1.10万规模的虚拟机,每一个虚拟机能够在不论什么一个计算节点上启动,该怎样做?计算,存储,网络都是怎么拉通与配合的? 2.用户怎样自己定义业务网络,怎样解决网络不够用的情况?正常就4096个vlan ...
- luogu3155 [CQOI2009]叶子的染色
题目大意 给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根.内部结点和叶子均可)着以黑色或白色.你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点( ...