1. 程式人生 > >每天一道博弈論之“E&D”

每天一道博弈論之“E&D”

lap cor mes log 成了 AI DC char src

題意:

給你n組石子,每組有兩摞。操作為選其中一摞石子分成兩摞,拋棄原來同一組的另一摞石子,直到無法操作。問給你這n組石子,問先手勝還是後手勝。 (比如(10,8,),你可以將10分為(5,5),那麽這一組石子就成了(5,5),那個8就可以不用管了)

題目鏈接:https://www.luogu.org/problemnew/show/P2148

題解:

其實這道題我也不是很會,看了題解才寫出來T_T

首先每組是獨立的,可以求出每一組的SG值然後用SG定理解決。

那麽每組的SG值怎麽求呢?

  好像沒有人會qwq 所以我們先打個表。比如下面這個10*10的表(第一行為列號)

i:0 1 2 3 4 5 6 7 8 9 10
i:1 0 1 0 2 0 1 0 3 0 1
i:2 1 1 2 2 1 1 3 3 1 1
i:3 0 2 0 2 0 3 0 3 0 2
i:4 2 2 2 2 3 3 3 3 2 2
i:5 0 1 0 3 0 1 0 3 0 1
i:6 1 1 3 3 1 1 3 3 1 1
i:7 0 3 0 3 0 3 0 3 0 4
i:8 3 3 3 3 3 3 3 3 4 4
i:9 0 1 0 2 0 1 0 4 0 1
i:10 1 1 2 2 1 1 4 4 1 1

  我(bie)們(ren)可以發現這麽一個規律:

  1,當i和j都是奇數時sg值為0 . 2,當i和j都為偶數是sg[i][j] = sg[i/2][j/2]+1

  3,當i,j一奇一偶時(假設i為奇數)sg[i][j] = sg[i+1][j+1]

  復雜度為O(t*n*logS)

  於是這道題就做完啦qwq

AC代碼:

技術分享圖片
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define RI register int
using namespace std;
const int INF = 0x7ffffff ;
const int N = 1e4 + 10 ;

inline 
int read() { int k = 0 , f = 1 ; char c = getchar() ; for( ; !isdigit(c) ; c = getchar()) if(c == -) f = -1 ; for( ; isdigit(c) ; c = getchar()) k = k*10 + c-0 ; return k*f ; } int n, hh[N] ; int ser(int x,int y) { if(x%2 && y%2) return 0 ; if(x%2 &&!(y%2)) return ser(x+1,y) ; if(!(x%2) && y%2) return ser(x,y+1) ; return ser(x>>1,y>>1)+1 ; } int main() { int t = read() ; while(t--) { int cnt = 0 ; n = read() ; n >>= 1 ; for(int i=1;i<=n;i++) { int a = read(), b = read() ; hh[++cnt] = ser(a,b) ; } int ans = 0 ; for(int i=1;i<=cnt;i++) ans ^= hh[i] ; if(ans) printf("YES\n") ; else printf("NO\n") ; } return 0 ; }
View Code

打表代碼:

技術分享圖片
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define RI register int
using namespace std;
const int INF = 0x7ffffff ;
const int N = 1000 + 10 ;

inline int read()
{
    int k = 0 , f = 1 ; char c = getchar() ;
    for( ; !isdigit(c) ; c = getchar())
      if(c == -) f = -1 ;
    for( ; isdigit(c) ; c = getchar())
      k = k*10 + c-0 ;
    return k*f ;
}

int sg[N][N] ; 

int ser(int x,int y) {
    if(sg[x][y]) return sg[x][y] ;
    bool vis[N] = {0} ;
    for(int i=1;i<x;i++) vis[ser(x-i,i)] = 1 ;
    for(int i=1;i<y;i++) vis[ser(y-i,i)] = 1 ;
    for(int i=0; ;i++)
      if(!vis[i]) {
          sg[x][y] = i ; return i ;
      } 
}

int main() {
    sg[1][1] = 0 ;
    int n = read() ;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) {
            sg[i][j] = ser(i,j) ;
        }
    }
    
    printf("i:0  ") ;
    for(int i=1;i<=n;i++) if(i<10) printf("%d ",i) ; else printf("%d",i) ; printf("\n") ; // 輸出列號 

    for(int i=1;i<=n;i++) {
        if(i<10) printf("i:%d  ",i) ; // 輸出行號 
        else printf("i:%d ",i) ; // 輸出行號(為了整齊 
        for(int j=1;j<=n;j++) {
            printf("%d ",sg[i][j]) ;
        }
        printf("\n") ;
    }
    return 0 ;
}
View Code

每天一道博弈論之“E&D”