博弈論(nim游戲,SG函數)


說到自己,就是個笑話。思考問題從不清晰,sg函數的問題證明方法就在眼前可卻要棄掉。不過自己理解的也並不透徹,做題也不太行。耳邊時不時會想起alf的:“行不行!”

基本的小概念

  • 這里我們討論的是公平游戲(ICG游戲:Impartial Combinatorial Games),滿足:
    1.雙方每步的限制相同(輪流)
    2.游戲有盡頭
  • 對於當前局面的玩家如果能有必勝策略,那就是N局面(反之,P局面)

SG函數

  • 每一種決策以及后面的所有可能可以抽象成有向無環圖,而sg函數的計算就類似圖上dp的過程。
  • 若當前的局面是now,下一步的局面為to
    \(SG[now]=Mex(SG[to])\)
    \(Mex(S)\)表示,最小的沒有在\(S\)集合里的非負整數
    這就是SG的定義了,這很迷糊,因為它就是一個當前局面的估價,很難說它具體是什么意義(甚至后面還會用它來運算就離譜)
  • 顯然的性質
    1.SG[now]后面存在了sg為0的局面,now就為N局面,你接下來你的操作也會move到\(sg[to]=0\)這種局面。
    2.1的反之。
    3.SG為0表示當前局面P,否則為N。(ps.三個點就是一個結論,3總結1,2)
  • Muiti-SG的性質(見nim游戲后)

nim游戲

  • 這是最基本最重要的游戲,理解它后,你將入門博弈。
  • 規則:兩個人玩nim游戲,有n堆石子,每堆有\(a_i\)個,每次可從一堆中取走任意多個石子。如果一個人無石子可取就輸。
  • 結論:\(xorsum\ =\ a_1\ xor\ a_2\ xor\ a_3\ xor\ ...\ xor\ a_n\)是0就為P局面,否則N局面。
  • 證明:如果xorsum=0,取后xorsum!=0; 如果xorsum!=0,一定存在從一堆(堆i)取走\(a_i-xorsum\ xor\ a_i\)個石子,使取后\(xorsum=0\)
    然后最終輸的狀態為全0,這是xorsum=0的。而如果一開始為xorsum=0一定被拿捏,一直xorsum=0直到輸掉(對面就一直使你xorsum=0)
  • 這時,你肯定會想:用sg暴力做呢?狀態存不下啊。當然就引出了后面有關sg的重要推論……
    不過對於只有一堆(或者想成n堆每堆獨立)\(sg[i]=i\)的。(i為還剩的石子個數)

重要推論

  • 從上面,或許我們能猜想:
    S局面被拆分成\(n\)相互獨立的小局面,(我們這里叫S為\(s_1->s_n\)的游戲和,滿足:
    \(SG[S]\ =\ SG[s_1]\ xor\ SG[s_2]\ ....\ xor\ SG[s_n]\)
    這個對於公平游戲來講,居然是正確的。證明?->
  • 證明:
    我們通常遇到博弈想最終態(P態為多,因為sg=0)
    當然是所有sg值全為0的時候。
    你每次只能轉移一個小局面(一堆石子),它能轉移到0~sg[i]-1以及sg[i]+k,k是不確定的。
    你發現如果沒有sg[i]+k我們可以用nim游戲來證明。
    如果有,相當於加石子,實質上還是符合nim游戲證明中的異或和總是=0和!=0相交替。
    加和減因此也沒有實質區別,就得證。
  • ps.老板告訴我們可以把任何ICG問題轉化為相同sg值(=石子個數)的nim游戲。

小結:

nim類,或者ICG類都感覺核心就用到以上幾個結論。不過我太菜了,還是做不來題。博弈論還有很多值得探索,不過現實中人也沒有那么理性,很多時候也用不到。

有趣題目:

1.字符游戲

  • 思路:首先01串+構造長度為L+前綴關系,很容易想到trie樹
    因為“互異”,所以每次更新trie樹從根開始的一條鏈,鏈葉的子樹里的點下次就不可以被選擇了。
    然后我們通過畫圖找規律,然后每個點的SG值為lowbit(子樹深度),畫一下就知道了
  • 代碼:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
int go[N][2],nd;
ll lowbit(ll u) {return u&(-u);}
ll ans=0;
char s[N];
void Insert() {
	int u=0,len=strlen(s);
	for(int i=0;i<len;i++) {
		int x=s[i]-'0';
		if(!go[u][x]) go[u][x]=++nd;
		u=go[u][x];
	}
}
void dfs(int u,ll dep) {
	if(go[u][0]) dfs(go[u][0],dep-1);
	else ans^=lowbit(dep);
	if(go[u][1]) dfs(go[u][1],dep-1);
	else ans^=lowbit(dep);
}
int main() {
	int n;ll l;
	scanf("%d%lld",&n,&l);
	for(int i=1;i<=n;i++) {
		scanf("%s",s);
		Insert();
	}
	dfs(0,l);
	if(ans)printf("Alice");
	else printf("Bob");
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM