SG函数相关
真是一个巨大的坑 noip之前还是填一填吧
概念
P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败。
N点:必胜点,处于此情况下,双方操作均正确的情况下必胜。
性质
- 所有终结点是 必败点 P 。(我们以此为基本前提进行推理,换句话说,我们以此为假设)
- 从任何必胜点N 操作,至少有一种方式可以进入必败点 P。反过来说,能递推到任一必败点的点均为必胜点
- 无论如何操作,必败点P 都只能进入 必胜点 N.
SG函数
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
对于任意状态 x . 定义 SG(x) = mex(S),其中 S 是 x 后继状态的SG函数值的集合。如 x 有三个后继状态分别为 SG(a),SG(b),SG(c),那么SG(x) = mex{SG(a),SG(b),SG(c)}。 这样 集合S 的终态必然是空集,所以SG函数的终态为 SG(x) = 0,当且仅当 x 为必败点P时。
以下sg不符定义,只是用来推状态的
EX1. Good Luck in CET-4 Everybody!
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 14211 Accepted Submission(s): 9057
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先抓牌,请问谁能赢呢?
Input
输入数据包含多个测试用例,每个测试用例占一行,包含一个整数n(1<=n<=1000)。
Output
如果Kiki能赢的话,请输出“Kiki”,否则请输出“Cici”,每个实例的输出占一行。
Sample Input
1
3
Sample Output
Kiki
Cici
用DP可以得出相关状态,0表示当前点未必败点,否则为必胜点
#include<cstdio>
const int N=1002;
int n,f[N],sg[N];
void getsg(int n){
for(int i=1;i<=n;i++){
sg[i]=0;
for(int j=0;f[j]<=i;j++)if(!sg[i-f[j]]){sg[i]=1;break;}
}
}
int main(){
f[0]=1;for(int i=1;i<=15;i++)f[i]=f[i-1]<<1;
getsg(1000);
while(scanf("%d",&n)!=EOF){
if(sg[n]!=0)puts("Kiki");else puts("Cici");
}
return 0;
}
可以AC了,但打表能找到规律的
对于3的倍数的数,无论如何操作,最后必定会到达3这点,因为每次只能减2的幂,那么显然这种情况下先手会败
然后又去看了hdu2147
打表
#include<cstdio>
const int N=2002;
int n,m,sg[N][N];
inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return a<b?a:b;}
inline void getsg(){
for(int s=2;s<=4000;s++)
for(int j,i=max(1,s-2000);i<=min(2000,s-1);i++){
j=s-i;
if(i-1>0&&j-1>0&&!sg[i-1][j-1]){sg[i][j]=1;continue;}
if(i-1>0&&!sg[i-1][j]){sg[i][j]=1;continue;}
if(j-1>0&&!sg[i][j-1]){sg[i][j]=1;continue;}
}
}
int main(){
freopen("sg.out","w",stdout);
getsg();
for(int i=1;i<=100;i++){
for(int j=1;j<=100;j++)printf("%d ",sg[i][j]);
puts("");
}
while(scanf("%d%d",&n,&m),n||m)if(sg[n][m])puts("Wonderful!");else puts("What a pity!");
return 0;
}
找规律
#include<cstdio>
int n,m;
int main(){
while(scanf("%d%d",&n,&m),n||m){
if((n&1)&&(m&1))puts("What a pity!");else puts("Wonderful!");
}
return 0;
}
hdu1848类似
#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");
else printf("Nacci\n");
}
return 0;
}

浙公网安备 33010602011771号