模板1如下(SG打表):
//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//hash[]:mex{}
int f[K],sg[N],hash[N];
void getSG(int n)
{
memset(sg,0,sizeof(sg));
for(int i=1; i<=n; i++) {
memset(hash,0,sizeof(hash));
for(int j=0; f[j]<=i && j < k; j++) //k是f[]的有效长度
hash[sg[i-f[j]]]=1;
for(int j=0; ; j++) { //求mes{}中未出现的最小的非负整数
if(hash[j]==0) {
sg[i]=j;
break;
}
}
}
}
模板2如下(dfs):
//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
//n是集合s的大小 S[i]是定义的特殊取法规则的数组
int s[N],sg[N],n;
int getSG(int x)
{
if(sg[x]!=-1)
return sg[x];
bool vis[M];
memset(vis,0,sizeof(vis));
for(int i=0; i<n; i++) {
if(x>=s[i])
vis[getSG(x-s[i])]=1;
}
for(i=0;; i++)
if(!vis[i]) {
sg[x]=i;
break;
}
return sg[x];
}
例一: hdu 1536 S-Nim
题意:首先输入K 表示一个集合的大小 之后输入集合 表示对于这对石子只能去这个集合中的元素的个数
之后输入 一个m 表示接下来对于这个集合要进行m次询问
之后m行 每行输入一个n 表示有n个堆 每堆有n1个石子 问这一行所表示的状态是赢还是输 如果赢输入W否则L
思路:对于n堆石子 可以分成n个游戏 之后把n个游戏合起来就好了
代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
typedef long long LL;
const int MAXN = 108;
int a[MAXN*MAXN], pre[MAXN], k;
void init() {
int f[MAXN];
for(int i = 1; i <= 10000; i++) {
a[i] = 0;
memset(f, 0, sizeof(f));
for(int j = 1; j <= k && pre[j] <= i; j++)
f[a[i-pre[j]]] = 1;
for(int j = 0; ; j++)
if (!f[j]) {
a[i] = j;
break;
}
}
}
int main() {
int m;
//freopen("in.txt", "r", stdin);
while (scanf("%d", &k) && k) {
for(int i = 1; i <= k; i++)
scanf("%d", pre+i);
sort(pre+1, pre+k+1);
init();
scanf("%d", &m);
while (m--) {
int n;
scanf("%d", &n);
int res = 0;
for(int i = 1; i <= n; i++) {
int t;
scanf("%d", &t);
res ^= a[t];
}
if (res) printf("W");
else printf("L");
}
puts("");
}
return 0;
}
例二:Gym - 101128GGame of Cards
题意:有n堆扑克牌,两个人轮流玩游戏,游戏规则:
先选一堆扑克牌,然后拿走堆顶0-k张,剩余的堆顶那一张牌上是几就必须再拿走几张,当某一方无牌可拿或者剩余张数不够必须拿走的张数时则该方输
代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
typedef long long LL;
const int MAXN = 1008;
int a[MAXN], k, val[MAXN];
void init() {
int f[22];
for(int i = 1; i <= 1000; i++) {
a[i] = 0;
memset(f, 0, sizeof(f));
for(int j = 0; j <= k; j++)
if (i-j-val[i-j] >= 0)//这里注意一下就好了;
f[a[i-j-val[i-j]]] = 1;
for(int j = 0; ; j++)
if (!f[j]) {
a[i] = j;
break;
}
}
}
int main() {
int m;
//freopen("in.txt", "r", stdin);
scanf("%d%d", &m, &k);
val[0] = 1;
int res = 0;
while (m--) {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", val+i);
init();
res ^= a[n];
}
if (res) printf("Alice can win.\n");
else printf("Bob will win.\n");
return 0;
}
sg函数巧用
hdu 1729 Stone Game
1、设当前的箱子容量为si,求出一个t满足:t + t * t < si,如果当前箱子里有ci颗石头,
1、ci > t 则必胜;
2、ci == t 则必败;
3、ci < t不能确定,将t作为si递归调用函数。
当满足ci > t时,return si - ci 作为当前状态的sg值。因为:
如图:
当ci在si点时,为有向图的端点,出度为0,也就是必败点,所以sg值为0;
当ci 位于si - 1时,ci的端点可能的sg值构成的集合为{0},所以当前sg值 为1;
当ci 位于si - 2 时,ci的端点可能的sg值构成的集合为{0, 1},所以当前的sg值为2;
。
。
。
可得,ci所在位置的sg值为si - ci;
代码:
//#include<bits/stdc++.h>
#include <iostream>
#include <string>
#include <queue>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN = 1000008;
int sg(int c, int s) {
int q = sqrt(s);
while (q*(q+1) >= s) q--;
if (c > q) return s-c;
else return sg(c, q);//和暴力不一样有点小灵活
}
int main() {
int n, p = 1;
while (scanf("%d", &n) && n) {
printf("Case %d:\n", p++);
int res = 0;
while (n--) {
int s, c;
scanf("%d%d", &s, &c);
res ^= sg(c, s);
}
if (res) puts("Yes");
else puts("No");
}
return 0;
}
sg 水水的;
hdu 1730 Northcott Game
要理解sg函数啊
代码:
//#include<bits/stdc++.h>
#include <iostream>
#include <string>
#include <queue>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN = 1000008;
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
int res = 0;
while (n--) {
int s, c;
scanf("%d%d", &s, &c);
res ^= int(abs(s-c)-1);
}
if (res) puts("I WIN!");
else puts("BAD LUCK!");
}
return 0;
}
hdu 2524 A Chess Game
题意:给一些点和有向边,组成一个有向无环图(不止一个头结点,因为这个wa一次.....);在一些点上放一些棋子,2个人进行游戏,每次进行的操作是根据有向边来移动一个棋子;直到没有棋子移动,那个人就输了;
题解:明显的sg函数
代码:
//#include<bits/stdc++.h>
#include <iostream>
#include <string>
#include <queue>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
typedef long long LL;
const int MAXN = 1008;
int sg[MAXN], adjList[MAXN][MAXN], adjN[MAXN];
int n;
int dfs(int k) {
int num[MAXN] = {0};
if (sg[k]) return sg[k];
for(int i = 0; i < adjN[k]; i++) {
num[dfs(adjList[k][i])] = 1;
}
for(int i = 0; ; i++)
if (!num[i]) return sg[k] = i;
}
int main() {
//freopen("in.txt", "r", stdin);
while (~scanf("%d", &n)) {
memset(sg, 0, sizeof(sg));
for(int i = 0; i < n; i++) {
scanf("%d", adjN+i);
for(int j = 0; j < adjN[i]; j++) {
scanf("%d", &adjList[i][j]);
}
}
for(int i = 0; i < n; i++)
dfs(i);
int m;
while(scanf("%d", &m) && m) {
int res = 0;
while(m--) {
int t;
scanf("%d", &t);
res ^= sg[t];
}
if (res) puts("WIN");
else puts("LOSE");
}
}
return 0;
}