1. 程式人生 > >sg函式的一些題

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

題意:有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;
}


相關推薦

sg函式一些

模板1如下(SG打表): //f[]:可以取走的石子個數   //sg[]:0~n的SG函式值   //hash[]:mex{}   int f[K],sg[N],hash[N];   void getSG(int n)   {           memset(sg,0,

關於SG函式一些淺顯的感性理解

有向圖博弈。給定一個有向圖,一個起點,起點上有一個棋子,兩個玩家輪流推動棋子,誰不能推了誰就輸了。希望知道某個點為起始是必勝還是必敗。 注:這兩個人都十分聰明,可以看透未來(?)。 一點分析: 1.顯然這要是個DAG,不然遊戲就不能結束了。。(廢話)  而且兩個人要在同

【男人八】 A.String Game(字尾自動機 + sg函式

題目大意:給一個模式串和n個它的子串,Alice和Bob玩遊戲,Alice先手,每回合任選一個子串,該回合輪到的人在它後面加一個字母,並且保證加了之後的新串仍然是模式串的子串。輪到後沒辦法保證上述新增要求的人輸。 (雖然題目沒有說,但是字符集是小寫字母) 可以

SG函式一些理解

前置技能:nim遊戲。 任何一個博弈遊戲我們都可以把它抽象為在一個有向無環圖上的博弈。每個節點像其能轉移的節點連一條有向邊。 現在我們現在開始玩一個遊戲來理解SG函式,n個石子每個人可以取走{1,3,4}個,不能取的人敗。 那麼我們把他抽象為一個圖。每個節點x向(x-1

多線程的一些,無聊的時候來做一做

獲得 end 如果 一個 ont 程序 負責 運行 cal 1.C 和 Java 都是多線程語言。( ) 2.如果線程死亡,它便不能運行。( ) 3.在 Java 中,高優先級的可運行線程會搶占低優先級線程。( ) 4.程序開發者必須創建一個線程去管理內存的分配。( )

hdu 5724 Chess (SG函式

題目連結:hdu 5724 題意:有一個n行20列的棋盤,棋盤上分佈著一些棋子,A、B兩人輪流下棋,A先手,每次操作可以將某個棋子放到自己右邊的第一個空位(也就是說右邊如果已經有子,可以跳過它,沒有就右移一步),但最多20列,絕對不能超過棋盤,無棋可走的輸。 題解:進行狀態壓縮,bit來

SG函式入門

參考部落格:https://baike.baidu.com/item/SG%E5%87%BD%E6%95%B0/1004609 https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html 主要參考百度百科: 首先定義mex(mi

Playing With Stones UVALive - 5059 Nim SG函式 打表找規律

Code: #include<cstdio> #include<algorithm> using namespace std; typedef long long ll; ll SG(ll i){ return i % 2 ==0 ? i / 2 : SG(i/2);

[BeiJing2009 WinterCamp]取石子游戲 Nim SG 函式

Code: #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define maxn 1003 int arr[13],step[13],SG[maxn]; bo

C++ sort()函式一些簡單的用法

C++標準函式模板庫(stl)裡有個自帶的排序函式sort(),該函式可以直接對陣列或者類似陣列型別的結構體進行排序,其時間複雜度為n*log2(n);sort()函式定義在標頭檔案<algorithm>中,基本用法為:sort(a,a+n);其中,a為一個數組的名稱,n為需

初學sg函式

轉載自部落格:https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html 在介紹SG函式和SG定理之前我們先介紹介紹必勝點與必敗點吧. 必勝點和必敗點的概念: P點:必敗點,換而言之,就是誰處於此位置,則在雙方操作正確的情況下必敗。

LightOJ 1315【Nim博弈】 二維SG函式與記憶化搜尋

A Hyper Knight is like a chess knight except it has some special moves that a regular knight cannot do. Alice and Bob are playing this game (you may w

[leetcode]樹求和的一些

1.sum-root-to-leaf-numbers 題目描述: Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.An example

【BZOJ1874】取石子游戲(SG函式

題意:小H和小Z正在玩一個取石子游戲。 取石子游戲的規則是這樣的,每個人每次可以從一堆石子中取出若干個石子, 每次取石子的個數有限制,誰不能取石子時就會輸掉遊戲。 小H先進行操作, 他想問你他是否有必勝策略,如果有,第一步如何取石子 n<=10,a[i]<=1000,m<=10,b[i

poj 2425 A Chess Game (SG函式

題目連結:poj 2425 題意:題目會給出一個有向無環圖,對於某個棋子,可以將它移動到其後繼棋子的任意一個位置,一個位置可以放多個棋子。 給出n個點,從0到n-1,接著n行,每行開始有Xi,代表第i個點後繼連線點有Xi個,分別是......。 緊接著有多組詢問,每組詢問的M代表有哪

A Chess Game POJ - 2425(SG函式

POJ 2425 題意: 一個有向無環圖,在若干點上有若干棋子,兩人輪流移動棋子,每次只能將一個棋子移動一步,當無棋子可移動時輸,即移動最後一枚棋子者勝; 思路: 假設只有一枚棋子,那麼對於一個點的勝負局面其實就是其SG值; 多枚棋子的勝負局面就是每個點的SG值異或和; 現在就是要求每個

POJ 2311 Cutting Game 博弈論 SG 函式的理解

傳送門 這題是對 SG 函式本質理解的一個絕佳的題目 之前我們做題的時候大多都是 Nim 博弈,想當然的認為 SG 函式用二進位制數表示一個局面用的是棋子個數,並且也只適用於這個局面,甚至認為 SG 函式只能解決 Nim 類的題目,並且想當然的認為最初狀態(即0的時候) SG = 0

函式分析

1. var a = 12 function fn() {   console.log(a)    var a = 45;   console.log(a)   } fn() 2. function fn() {   console.log(11)   function ff() {     console

poj 2960 S-Nim【SG函式

預處理出SG函式,然後像普通nim一樣做即可 #include<iostream> #include<cstdio> using namespace std; const int N=10005; int k,s[N],m,n,sg[N],v[N],ti,ans; int read(

hdu 1848 博弈sg函式打表

這道題是典型的sg函式來解決的博弈問題。 先給出了可以移動石子數量的取值區間就是一個一千以下的斐波那契數列,這個好辦,直接一個數組完事。 然後給出三堆石子的數量,m,n,p; 我們的思路就是把1000以下的所有數的sg值都打表出來。 然後再將m n p三個數的sg值異