日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

博弈(sg函数

發布時間:2023/12/10 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 博弈(sg函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

繁繁的博弈博客
巨佬整理的sg函數題目

SG函數

SGSGSG 函數是對游戲圖中每一個節點的評估函數。

SG(x)=0SG(x)=0SG(x)=0 則為必敗狀態,若 SG(x)≠0S G ( x ) ≠ 0SG(x)?=0,則為必勝狀態

SG定理:SG定理:SG所有一般勝利下的公平組合游戲都能轉化成尼姆數表達的尼姆堆博弈,一個博弈的尼姆值 定義為這個博弈的等價尼姆數,即:對于當前游戲 XXX,它可以被拆分為多個子游戲 x1,x2,...xnx_1,x_2,...x_nx1?,x2?,...xn?,那么 SG(X)=SG(x1)?SG(x2)?SG(x3)...?SG(xn)SG(X)=SG(x_1)\bigoplus SG(x_2) \bigoplus SG(x_3)...\bigoplus SG(x_n)SG(X)=SG(x1?)?SG(x2?)?SG(x3?)...?SG(xn?)

對于由 nnn 個有向圖游戲組成的組合游戲 設它們的起點分別為 s1,s2,s3,...sns_1,s_2,s_3,...s_ns1?,s2?,s3?,...sn?先手勝利時當且僅當 SG(s1)?SG(s2)?SG(s3)...?SG(sn)≠0SG(s_1) \bigoplus SG(s_2) \bigoplus SG(s_3)...\bigoplus SG(s_n) \neq 0SG(s1?)?SG(s2?)?SG(s3?)...?SG(sn?)?=0

SGSGSG 函數
xxx 的后繼狀態 y1,y2,...yny_1,y_2,...y_ny1?,y2?,...yn?
SG(x)=mex(SG(y1),SG(y2),...SG(yn))SG(x)=mex(SG(y_1),SG(y_2),...SG(y_n))SG(x)=mex(SG(y1?),SG(y2?),...SG(yn?))

S-Nim
題意:
給定一個大小為 nnn 的集合 SSSqqq 組查詢,每次查詢給定 mmm 堆石頭,每堆石頭的個數 xix_ixi?,兩人輪流從中取石頭,每次取石頭的個數來自集合 SSS,問先拿者是否有必勝策略
n<=100,Si<=10000,q<=100,m<=100,xi<=10000n<=100,S_i<=10000,q<=100,m<=100,x_i<=10000n<=100,Si?<=10000,q<=100,m<=100,xi?<=10000
思路:
SGSGSG 函數,多個游戲
sgsgsg 函數經典例題
code:

#include<iostream> #include<cstring> #include<string> #include<cmath> #include<cstdio> #include<algorithm> #include<vector>#define endl '\n' #define ll long long #define ull unsigned long long #define ld long double #define all(x) x.begin(), x.end() #define mem(x, d) memset(x, d, sizeof(x)) #define eps 1e-6 using namespace std; const int maxn = 1e4 + 9; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; ll n, m; int a[maxn], sg[maxn]; bool vis[maxn];void work() {mem(sg, 0);for(int i = 1; i <= n; ++i) cin >> a[i];for(int i = 1; i <= maxn - 9; ++i){// 預處理石子數為i的sg函數for(int j = 1; j <= n; ++j) if(i >= a[j]) vis[sg[i-a[j]]] = 1;// 標記后繼狀態sg函數值while(vis[sg[i]]) ++sg[i];// 求mexfor(int j = 1; j <= n; ++j) if(i >= a[j]) vis[sg[i-a[j]]] = 0;// 消除標記}int q;cin >> q;while(q--){cin >> m;int ans = 0;for(int i = 1, x; i <= m; ++i){cin >> x;ans ^= sg[x];// 子游戲sg函數值異或一遍}cout << (ans ? "W" : "L");}cout << endl; }int main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); // int TT;cin>>TT;while(TT--)while(cin >> n && n)work();return 0; }

黑黑白白
思路:
SGSGSG 函數,單個游戲
sg[x]sg[x]sg[x] 表示先手輪到 xxx 點時的取勝情況,sg[x]=1sg[x]=1sg[x]=1 表示必勝狀態

  • 當子狀態有必敗狀態時,該狀態為必勝狀態。
  • 當子狀態全部為必勝狀態,該狀態為必敗狀態。

遍歷這棵樹,把所有孩子結點的答案向上合并
code:

#include<bits/stdc++.h> #define endl '\n' #define ll long long #define ull unsigned long long #define ld long double #define all(x) x.begin(), x.end() #define mem(x, d) memset(x, d, sizeof(x)) #define eps 1e-6 using namespace std; const int maxn = 2e6 + 9; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; ll n, m; vector <int> e[maxn]; int sg[maxn]; void dfs(int x, int fa){bool f = 1;//因為判斷必勝狀態的條件是存在某個子狀態,根據定義的sg函數含義,初始設為1for(auto to : e[x]) if(to != fa){dfs(to, x);f &= sg[to];// 企圖&出來0}sg[x] = !f; } void work() {cin >> n >> m;for(int i = 1; i <= n; ++i) e[i].clear(), sg[i] = 0;for(int i = 1; i < n; ++i){int x, y;cin >> x >> y;e[x].push_back(y);e[y].push_back(x);}dfs(m, m);cout << (sg[m] ? "Gen" : "Dui") << endl; }int main() {ios::sync_with_stdio(0);int TT;cin>>TT;while(TT--)work();return 0; }

春聯
思路:
SGSGSG 函數,單個游戲
題解
看了下題解,就是用最后一個字符往前推必勝和必敗點
code:

#include<bits/stdc++.h> #define endl '\n' #define ll long long #define ull unsigned long long #define ld long double #define all(x) x.begin(), x.end() #define mem(x, d) memset(x, d, sizeof(x)) #define eps 1e-6 using namespace std; const int maxn = 2e6 + 9; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; ll n, m, k; int sg[maxn], mp[30], vis[30]; string s; int ans = 0; int get(){mem(vis, 0);for(int i = 1; i <= 26; ++i){if(mp[i] != n + 1) vis[sg[mp[i]]] = 1;}for(int i = 0; ; ++i) if(!vis[i]) return i; } void work() {cin >> s;n = s.size();s = "@" + s;for(int i = 1; i <= 26; ++i) mp[i] = n + 1; for(int i = n; i >= 1; --i){s[i] -= 'a' - 1;sg[i] = get();mp[s[i]] = i;}cout << (get() ? "kou" : "yukari"); }int main() {ios::sync_with_stdio(0); // int TT;cin>>TT;while(TT--)work();return 0; }

寒冬信使2
思路:
SGSGSG 函數,單個游戲
www 看成 111, bbb 看成 000, 可以得到一個 010101 串, 把狀態壓縮成 X=∑i=0n?12i?(si==′w′)X=\sum_{i=0}^{n-1}2^i*(s_{i}=='w')X=i=0n?1?2i?(si?==w)
可以發現操作只會讓 XXX 的值減少或者不變,于是可以記錄每個狀態的 SGSGSG 函數,XXXSGSGSG 函數為 000 輸出 NoNoNo,否則輸出 YesYesYes
code:

#include<bits/stdc++.h> #define endl '\n' #define ll long long #define ull unsigned long long #define ld long double #define all(x) x.begin(), x.end() #define mem(x, d) memset(x, d, sizeof(x)) #define eps 1e-6 using namespace std; const int maxn = 2e6 + 9; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; ll n, m; string s; bitset <109> vis; int sg[maxn]; void init(){for(int i = 1; i < 1 << 10; ++i)// 二進制枚舉預處理所有01串狀態{vis.reset();for(int j = 0; j < 10; ++j){if(i & (1 << j))// 第j位為1 {if(j == 0) vis[sg[i ^ (1 << j)]] = 1;//操作二 else {for(int k = 0; k < j; ++k) vis[sg[i ^ (1 << j) ^ (1 << k)]] = 1;// 操作一 }}while(vis[sg[i]]) ++sg[i];//狀態i的sg函數 }} } void work() {cin >> n >> s;int st = 0;for(int i = 0; i < n; ++i) if(s[i] == 'w') st |= 1 << (i);cout << (sg[st] ? "Yes" : "No") << endl; }int main() {ios::sync_with_stdio(0);init();int TT;cin>>TT;while(TT--)work();return 0; }

D 與 S
思路:
kkk 個關鍵點是必勝點,如果一個節點直接連著連著兩個必勝點,那么這個點就會變成必勝點,把初始的 kkk 個點放入隊列不斷更新即可
code:

#include<bits/stdc++.h> #define endl '\n' #define ll long long #define ull unsigned long long #define ld long double #define all(x) x.begin(), x.end() #define mem(x, d) memset(x, d, sizeof(x)) #define eps 1e-6 using namespace std; const int maxn = 1e6 + 9; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; ll n, m, k; vector <int> e[maxn]; int sg[maxn], deg[maxn];void work() {cin >> n >> m >> k;for(int i = 1; i <= n; ++i) e[i].clear(), sg[i] = deg[i] = 0;queue <int> q;for(int i = 1; i <= k; ++i){int x;cin >> x;sg[x] = 1;q.push(x);}for(int i = 1; i <= m; ++i){int x, y;cin >> x >> y;e[x].push_back(y);e[y].push_back(x);}while(!q.empty()){int x = q.front();q.pop();for(auto to : e[x]){if(sg[to]) continue;++deg[to];if(deg[to] >= 2){sg[to] = 1;q.push(to);}}}cout << (sg[1] ? "yes" : "no") << endl; }int main() {ios::sync_with_stdio(0);int TT;cin>>TT;while(TT--)work();return 0; }

Anti-SG游戲

SJ定理:SJ定理:SJ: 對于任意一個 Anti?SGAnti - SGAnti?SG 游戲,如果我們規定:當局面中所有的單一游戲的 SGSGSG 值為 000 時,游戲結束,則先手必勝當且僅當:

  • 游戲的 SGSGSG 函數值不為 000 且游戲中某個單一游戲的 SGSGSG 函數值大于 111
  • 游戲的 SGSGSG 函數值為 000 且游戲中沒有任意一個單一游戲的 SGSGSG 函數值大于 111

P4279 [SHOI2008]小約翰的游戲
code:

#include<bits/stdc++.h> #define endl '\n' #define ll long long #define ull unsigned long long #define ld long double #define all(x) x.begin(), x.end() #define mem(x, d) memset(x, d, sizeof(x)) #define eps 1e-6 using namespace std; const int maxn = 2e6 + 9; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; ll n, m, k; int a[maxn];void work() {cin >> n;int sg = 0;bool f = 0;for(int i = 1; i <= n; ++i){cin >> a[i];sg ^= a[i];if(a[i] > 1) f = 1;}if(!f && !sg || f && sg) cout << "John\n";else cout << "Brother\n"; }int main() {ios::sync_with_stdio(0);int TT;cin>>TT;while(TT--)work();return 0; }

Multi - SG游戲

條紋 黑暗爆炸 - 2940
題意:

思路:
具體遞推公式好像需要打表找規律
code:

#include<bits/stdc++.h> #define endl '\n' #define ll long long #define ull unsigned long long #define ld long double #define all(x) x.begin(), x.end() #define mem(x, d) memset(x, d, sizeof(x)) #define eps 1e-6 using namespace std; const int maxn = 1e3 + 9; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; ll n, m; int a, b, c; bool vis[maxn]; int sg[maxn]; void work() {cin >> a >> b >> c;if(a > b) swap(a, b); if(b > c) swap(b, c); if(a > b) swap(a, b);for(int i = a; i <= maxn - 9; ++i){for(int j = 0; j + a <= i; ++j) vis[sg[j]^sg[i-j-a]] = 1;for(int j = 0; j + b <= i; ++j) vis[sg[j]^sg[i-j-b]] = 1;for(int j = 0; j + c <= i; ++j) vis[sg[j]^sg[i-j-c]] = 1;while(vis[sg[i]]) ++sg[i];for(int j = 0; j + a <= i; ++j) vis[sg[j]^sg[i-j-a]] = 0;for(int j = 0; j + b <= i; ++j) vis[sg[j]^sg[i-j-b]] = 0;for(int j = 0; j + c <= i; ++j) vis[sg[j]^sg[i-j-c]] = 0;}cin >> m;while(m--){cin >> n;cout << (sg[n] ? 1 : 2) << endl;} }int main() {ios::sync_with_stdio(0); // int TT;cin>>TT;while(TT--)work();return 0; }

總結

以上是生活随笔為你收集整理的博弈(sg函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。