sg函数的一些题

模板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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值