博弈論學習筆記

決定近段時間復習一下博弈論順便寫點筆記。

大佬博客:幾種常見博弈模型https://blog.csdn.net/wr132/article/details/51213331

SG函數與SG定理https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html

無敵的博弈總結https://blog.csdn.net/acm_cxlove/article/details/7854526

?

常見博弈模型

首先要清楚博弈論的基礎知識概念(必勝必敗態,多堆游戲,公平組合游戲,有向圖游戲),然后了解幾種常見的博弈模型,其中中比較常見的應該就是巴什博弈和尼姆博弈了。把一些題目轉換為經典的博弈論模型能夠快速地找到規律解決問題。

巴什博弈:只有一堆n個物品,兩個人輪流從這堆物品中取物,規定每次至少取一個,最多取m個,最后取光者得勝。n%(m+1)=0,是必先手敗的局勢。

威佐夫博奕:有兩堆各若干個物品,兩個人輪流從某一堆或同時從兩堆中取同樣多的物品,規定每次至少取一個,多者不限,最后取光者得勝。奇異局勢:ak?=[k(1+√5)/2],bk=?ak?+?k先手必敗。

尼姆博弈:有三堆各若干個物品,兩個人輪流從某一堆取任意多的物品,規定每次至少取一個,多者不限,最后取光者得勝。(a,b,c)是必敗態等價于a^b^c=0。

?HDU-2149

巴什博弈裸題,問是否先手必勝,是的話輸出先手第一次出手的必勝策略。m%(n+1)不為0先手必勝,必勝策略分兩種如果m<=n那么先手就可以一次勝利,否則就第一次就要出m%(n+1)。

#include<bits/stdc++.h>
using namespace std;int main()
{int m,n;while (scanf("%d%d",&m,&n)==2) {if (m%(n+1)==0) {puts("none"); continue;}if (m<=n) {printf("%d",m); for (int i=m+1;i<=n;i++) printf(" %d",i);} else {printf("%d",m%(n+1));}puts("");}return 0;
}
View Code

POJ-1067

威佐夫博奕裸題,只有奇異局勢的時候是必敗態,奇異局勢的公式為 ak?=[k(1+√5)/2],bk=?ak?+?k 。我們怎么判斷呢?用bk-ak得到k,然后用這個k計算得到ak看看和原ak是否相等。

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;int main()
{int n,m;while (scanf("%d%d",&n,&m)==2) {if (m>n) swap(n,m);int k=n-m;int tmp=floor(k*(1.0+sqrt(5.0))/2);if (tmp==m) puts("0"); else puts("1");}return 0;
}
View Code

HDU-1847

巴什博弈變形。我們觀察到3是一個先手必敗點,所以大家都避免走到3,但是只要有一個人避免了必敗點他就可以通過對方出的數湊成3的倍數讓對方必敗。所以n是3的倍數時候先手必敗否則先手必勝。

#include<bits/stdc++.h>
using namespace std;int main()
{int n;while (scanf("%d",&n)==1) {if (n%3) puts("Kiki"); else puts("Cici");}return 0;
}
View Code

?

?

?SG函數與SG定理

但是對于一些非常見博弈模型或者是模型變形規律不明顯的,我們有一個大殺器:SG函數。通過SG函數我們能夠快速直到某個點是必勝/必敗態從而找到規律。

做了HDU-1848當作求SG函數的模板:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1e3+10;
 4 int f[30],S[N],SG[N]; 
 5 
 6 void getSG(int n) {
 7     memset(SG,0,sizeof(SG));
 8     for (int i=1;i<=n;i++) {
 9         memset(S,0,sizeof(S));  //后繼SG狀態函數集合 
10         for (int j=0;j<=20&&f[j]<=i;j++) S[SG[i-f[j]]]=1;  //標記后繼狀態SG函數 
11         for (int j=0;;j++) 
12             if (!S[j]) {  //mex:后繼SG第一個沒出現的 
13                 SG[i]=j; break;
14             }
15     }
16 }
17 
18 int main()
19 {
20     int n,m,k;
21     f[0]=f[1]=1;
22     for (int i=2;i<=20;i++) f[i]=f[i-1]+f[i-2];  //求出可操作集 
23     getSG(1000);  //計算前1000的SG函數 
24     while (scanf("%d%d%d",&n,&m,&k) && (n||m||k)) {
25         if (SG[n]^SG[m]^SG[k]) printf("Fibo\n");  //多堆游戲XOR和 
26         else printf("Nacci\n");
27     }
28     return 0;
29 }
View Code

?HDU-1536

SG函數直接應用。要注意這題輸入的操作集合不是升序的要先排序才行。

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int k,n,m,f[110],S[N],SG[N]; void getSG(int n) {memset(SG,0,sizeof(SG));memset(S,-1,sizeof(S));for (int i=1;i<=n;i++) {for (int j=1;j<=k&&f[j]<=i;j++) S[SG[i-f[j]]]=i;  for (int j=0;;j++) if (S[j]!=i) {  SG[i]=j; break;}}
}int main()
{while (scanf("%d",&k) && k) {for (int i=1;i<=k;i++) scanf("%d",&f[i]);sort(f+1,f+k+1);getSG(10000);scanf("%d",&n);for (int i=1;i<=n;i++) {int m,tmp=0; scanf("%d",&m);for (int j=1;j<=m;j++) {int x; scanf("%d",&x);tmp^=SG[x];}if (tmp==0) printf("L"); else printf("W"); }puts("");}return 0;
}
View Code

HDU-3980

這道題會難一些。首先原來是一個環不好處理,但是我們注意到只要第一步下手后就會變成鏈,所以我們一開始從n-m這條鏈開始求SG函數。那么假設此時是一條長度為n的鏈,我們在當前選手在上面選擇了長度為m的區間染色,之后這條鏈就會分割成兩段分別是i和n-i-m的后繼狀態,于是此時就會變成兩個游戲,然后根據多堆Nim博弈異或和的結論,后繼狀態的SG函數就是SG[i]^SG[n-i-m],所以我們就能標記所有后繼的SG函數得到當前狀態的SG函數。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m,SG[N],S[N];int getSG(int n) {if (n<m) return 0;if (SG[n]!=-1) return SG[n];for (int i=0;n-i-m>=0;i++) S[getSG(i)^getSG(n-i-m)]=n;  //Nim博弈 for (int i=0;;i++)if (S[i]!=n) {SG[n]=i; break;} return SG[n];    
}int main()
{int T,Case=0; cin>>T;while (T--) {scanf("%d%d",&n,&m);memset(SG,-1,sizeof(SG));memset(S,-1,sizeof(-1));SG[0]=0;if (n<m || getSG(n-m)) printf("Case #%d: abcdxyzk\n",++Case);else printf("Case #%d: aekdycoin\n",++Case);}return 0;
}
View Code

?POJ-2505

SG函數應該很容易求。但是此題的數字非常大數組決定存不下,可以考慮用map計算SG函數可以獲得AC。

#include<cstdio>
#include<iostream>
#include<map>
using namespace std;
typedef long long LL;
LL n;
map<LL,LL> SG;LL getSG(LL x) {if (x>=n) return 0;if (SG.count(x)) return SG[x];map<LL,int> S;for (int i=2;i<=9;i++) S[getSG(x*i)]=1;for (int i=0;;i++)if (!S.count(i)) return SG[x]=i;
}int main()
{while (scanf("%lld",&n)==1) {SG.clear();if (getSG(1)) printf("Stan wins.\n"); else printf("Ollie wins.\n");}return 0;
}
View Code

?

其他題目練習:

POJ-2484

很經典的一道題。給n個連成環的硬幣,每次每人可以取一個或者相鄰的兩個硬幣,最后取不到硬幣的人輸掉。這題要運用一種平均分然后不斷模仿對方決策得到必勝的思想。首先如果硬幣個數<=2先手必勝,大于3的時候第一個人取完之后環會變成一條鏈,然后無論第一個人取的是一個還是兩個第二個人都可以根據第一個人取的數量和位置做出“把剩下硬幣取成完全相同的兩條鏈”(這里要重點理解)。然后通過模仿對方達到不敗之地。所以硬幣數量>=3時候先手必敗。

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e6+10;
int n,m;int main()
{while (scanf("%d",&n)==1 && n) {if (n<=2) puts("Alice");else puts("Bob");}return 0;
}
View Code

POJ-2425

圖上的博弈:一個n個點的有向無環圖,有某些點有硬幣,每次沒人只可以把一個棋子往前推一步(當然是沿著邊的方向推),最后不能推的人輸掉。盡管是在DAG上的博弈,但是SG函數的求法還是那樣,標記所有后繼狀態之后mex。然后有幾個棋子就是XOR和即可。

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int N=1e3+10;
int n,m,q,SG[N];
vector<int> G[N];int getSG(int x) {if (SG[x]!=-1) return SG[x];int S[N]={0};for (int i=0;i<G[x].size();i++) {int y=G[x][i];S[getSG(y)]=1;}for (int i=0;;i++)if (S[i]!=1) return SG[x]=i;
}int main()
{while (scanf("%d",&n)==1) {for (int i=1;i<=n;i++) {G[i].clear();int t; scanf("%d",&t);for (int j=1;j<=t;j++) {int x; scanf("%d",&x); x++;G[i].push_back(x);}}memset(SG,-1,sizeof(SG));while (scanf("%d",&q) && q) {int ans=0;for (int j=1;j<=q;j++) {int x; scanf("%d",&x); x++;ans^=getSG(x);}printf("%s\n",ans?"WIN":"LOSE");}}return 0;
}
View Code

POJ 1704 Georgia and Bob

一列格子上有n個棋子,每次沒人可以選擇一個棋子往左移動若干步,棋子不能越過1格子,棋子也不能越過其他棋子移動,不能移動的人輸掉。這題真的挺巧妙的,聽大佬說這是一種叫階梯博弈的博弈模型。我們先考慮對于一組相鄰的棋子,這種情況肯定是先手必敗,因為先手移動x格同時后手可以用后面那個棋子移動相同格。那么我們把棋子兩兩配對,那么兩兩配對的時候前面棋子的移動其實是沒有意義的,因為與其配對的后面棋子可以做一模一樣的操作。所以唯一的變數變成了兩兩配對的一組棋子間的間隙。

所以解法就是把棋子兩兩匹配,奇數時候第一個棋子和格子1匹配,然后對于每一組格子間的間隙當成一堆Nim游戲,求出所有Nim游戲的和就是答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int n,a[N];int main()
{int T; cin>>T;while (T--) {scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d",&a[i]);sort(a+1,a+n+1);int ans=0;for (int i=n;i>0;i-=2) ans^=a[i]-a[i-1]-1;printf("%s\n",ans?"Georgia will win":"Bob will win");}return 0;
}
View Code

?


POJ 1740 A New Stone Game
POJ 2068 Nim
POJ 3480 John
POJ 2348 Euclid's Game
HOJ 2645 WNim
POJ 3710 Christmas Game?
POJ 3533 Light Switching Game

HOJ 4388 Stone Game II

ZJU 3057 beans game

轉載于:https://www.cnblogs.com/clno1/p/11231919.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247380.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247380.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247380.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Promise鏈式調用和解決回調地獄的ES7終極解決方法async,await

promise鏈式調用 **then 是成功回調&#xff0c;只要在then后邊return一個promise就可以繼續then**<script type"text/javascript">let p1new Promise(function(resolve,reject){setTimeout(function(){resolve()//成功回調// reject()//失敗回調},2000)//2秒…

1.MySQL目錄結構

分為兩個目錄&#xff1a; 1.安裝目錄&#xff1a; 2.數據目錄&#xff1a; 在Linux下 yum安裝mysql后使用 find / -name my.cnf 找到mysql數據文件的位置 轉載于:https://www.cnblogs.com/sdrbg/p/11237231.html

對promise.all底層的實現的研究

1.Promise.all(iterable)返回一個新的Promise實例,此實例在iterable參數內素有的Promise都fulfilled或者參數中不包含Promise時&#xff0c;狀態變成fulfilled。 如果參數中Promise有一個失敗rejected &#xff0c;此實例回調失敗&#xff0c;失敗原因的是第一個失敗Promise的返…

vue-provide/inject輕松實現跨級訪問祖先組件

provide&#xff1a;Object | () > Object inject&#xff1a;Array<string> | { [key: string]: string | Symbol | Object } provide 和 inject 主要為高階插件/組件庫提供用例。并不推薦直接用于應用程序代碼中。是2.2.0版本 新增的。 這對選項需要一起使用&#x…

python 操作redis,存取為字節格式,避免轉碼加

這種情況連接數據庫&#xff0c;對數據的存取都是字節類型&#xff0c;存取時還得轉碼一下 from redis import Redis# 實例化redis對象rdb Redis(hostlocalhost, port6379, db0,passwordaaa123) rdb.set(name, root) name rdb.get(name) print(name)為了避免上述情況&#x…

element ui table scrollTop 滾動到行頭或行尾

1.設置table的ref為tableList2.設置滾動至頂部this.$refs.tableList.bodyWrapper.scrollTop 0;3.設置滾動至底部this.$refs.tableList.bodyWrapper.scrollTop this.$refs.tableList.bodyWrapper.scrollHeight;//如果請求完更新數據&#xff0c;需要使用$nextTick this.$nextTic…

Element-UI中Drawer抽屜去除標題自帶藍色框

當點擊事件drawertrue時&#xff0c;抽匣回打開 這時抽匣的標題會出現一個難看的藍色邊框&#xff0c;一會就會消失&#xff0c;但是好丑&#xff0c;所以要去掉它 解決方法 /deep/ :focus {outline: 0;} vue組件中&#xff0c;在style設置為scoped的時候&#xff0c;里面在…

Java生鮮電商平臺-B2B生鮮的互聯網思維

Java生鮮電商平臺-B2B生鮮的互聯網思維 在互聯網高速發展的今天&#xff0c;為我們的生活帶來了眾多便利。然而互聯網從早期的萌芽狀態到現在婦孺皆知&#xff0c;它的崛起速度遠遠超乎世人的想象。人們開始關注互聯網并且研究它&#xff0c;而思維模式的研究則是從深層次研究事…

Java生鮮電商平臺-高并發核心技術訂單與庫存實戰

Java生鮮電商平臺-高并發核心技術訂單與庫存實戰 一、 問題 一件商品只有100個庫存&#xff0c;現在有1000或者更多的用戶來購買&#xff0c;每個用戶計劃同時購買1個到幾個不等商品。 如何保證庫存在高并發的場景下是安全的&#xff1f; &#xff08;1&#xff09;不多發 &…

Vue2 MVVM 雙向綁定(數據劫持+發布者-訂閱者模式)

參考文獻&#xff1a;https://www.cnblogs.com/libin-1/p/6893712.html https://juejin.im/post/5b2f0769e51d45589f46949e MVVM拆開來即為Model-View-ViewModel&#xff0c;有View&#xff0c;ViewModel&#xff0c;Model三部分組成。View層代表的是視圖、模版&#xff0c;負…

常用的激活函數

1.階躍函數 &#xff0c;值域{0,1} 1 def step_function(x): 2 return np.array(x>0,dtypenp.int) 2.sigmoid函數 &#xff0c;值域(0,1) 1 def sigmoid(x): 2 return 1/(1np.exp(-x)) 3.relu函數 &#xff0c;值域[0&#xff0c;∞&#xff09; 1 def relu(x): 2 …

前端優化-vue-cli4安裝webpack-bundle-analyzer分析包文件

使用vue-cli3創建了一個工程目錄&#xff0c;技術棧為vue-cli3vue-routervuexelement-uiv-chartsaxios。但是等到項目開發完后&#xff0c;發現生成的app.js特別大&#xff0c;接近10M。為了優化項目性能&#xff0c;需要使用webpack-bundle-analyzer分析包文件&#xff0c;找出…

今天剛查到的宏,學習

PVP常用的宏&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&…

不要再問我三次握手和四次揮手

三次握手和四次揮手是各個公司常見的考點&#xff0c;也具有一定的水平區分度&#xff0c;也被一些面試官作為熱身題。很多小伙伴說這個問題剛開始回答的挺好&#xff0c;但是后面越回答越冒冷汗&#xff0c;最后就歇菜了。 見過比較典型的面試場景是這樣的: 面試官&#xff1…

Apache ServiceComb

Apache ServiceComb 開源&#xff0c;全棧微服務解決方案。開箱即用&#xff0c;高性能&#xff0c;兼容流行的生態&#xff0c;多語言支持 ServiceComb是一個微服務框架&#xff0c;提供服務注冊&#xff0c;發現&#xff0c;配置和管理實用程序。 下載 &#xff1a;http://se…

VScode PowerShell運行腳本報錯禁止運行腳本解決方式圖文

今天在新Windows電腦上用VScode的終端PowerShell運行一個腳本的時候&#xff0c; 錯誤 在vscode終端運行vue -V查看版本失敗 PS C:\Users\11388> vue -V vue : 無法加載文件 E:\NodeJs\node_global\vue.ps1&#xff0c;因為在此系統上禁止運行腳本。有關詳細信息&#xf…

多線程的創建方式---繼承Thread和實現Runnable

繼承Thread類創建多線程 1 package cn.ftf.thread;2 /**3 * 多線程實現方式一 繼承Thread實現多線程&#xff0c;繼承Thread&#xff0c;重寫run方法4 * author 房廷飛5 *6 */7 public class StartThread extends Thread{ //對象繼承Thread8 public static void mai…

添加右鍵用Sublime Text3 打開文件和文件夾

最近重新裝了一下系統&#xff0c;右鍵沒有用Sublime Text 3打開的選項了&#xff0c;于是查了一下解決方案 1、環境 Win10和Win7都可以Sublime Text 3最新版本以下為Win10系統下截圖 2、添加右鍵打開文件 Win R&#xff0c;輸入regedit,打開注冊表 找到HKEY_CLASSESS_ROOT…

Windows Mobile Widget Emulator

今天Vimpyboy 在codeplex發布了Windows Mobile Widget Emulator。這是一個用來調試Windows Mobile 6.5 Widget的工具&#xff0c;我在做Windows Mobile 6.5 新功能widget開發 的時候就發現調試Widget很麻煩。也有想法做一個Emulator&#xff0c;其實這個Emulator目標很明顯&…

JS 常用字符串數組遍歷函數方法整理

目錄 一、concat() 二、join() 三、push() 四、pop() 五、shift() 六、unshift() 七、slice() 九、substring() 和 substr() 十、sort 排序 十一、reverse() 十二、indexOf 和 lastIndexOf 十三、every 十四、some 十五、filter 十六、map ES6新增新操作數組的…