sg

博弈论初识

喜欢而已 提交于 2019-12-25 09:37:42
概念: P 点 —— 即必败点,某玩家位于此点,只要对方无失误,则必败; N 点 —— 即必胜点,某玩家位于此点,只要自己无失误,则必胜。 定理: 一、 所有终结点都是必败点 P (上游戏中,轮到谁拿牌,还剩 0 张牌的时候,此人就输了,因为无牌可取); 二、所有一步能走到必败点 P 的就是 N 点;   三、通过一步操作只能到 N 点的就是 P 点; 又即:只要当前状态可以转移到的状态中有一个是败态,那么当前状态就是胜态。 如果当前状态可以转移到的所有状态都是胜态,那么当前状态就是败态。 Bash Game : 只有一堆 n 个物品,两个人轮流从中取物,规定每次最少取一个,最多取 m 个,最后取光者为胜。 1 ,如果 n=m+1 ,那么由于一次最多只能取 m 个,所以,无论先取者拿走多少个, 后取者都能够一次拿走剩余的物品,后者取胜。必败 2 ,法则:如果 n= ( m+1)*r+s ,( r 为任意自然数, s ≤ m), 那么先取者要拿走 s 个物品,如果后取者拿走 k (≤ m) 个, 那么先取者再拿走m+1-k 个,结果剩下( m+1 )( r-1 )个,以后保持这样的取法,那么先取者肯定获胜。 总之,要保持给对手留下(m+1 )的倍数,就能最后获胜。必胜局 Wythoff Game: 有两堆各若干个物品,每个人每次可以从一堆里取任意多的物品,或同时从两堆中取同样多的物品

acm博弈论基础总结

空扰寡人 提交于 2019-12-05 19:53:57
acm博弈论基础总结 常见博弈结论 Nim 问题 : 共有 N 堆石子,编号 1..n ,第 i 堆中有个 a[i] 个石子。 每一次操作 Alice 和 Bob 可以从任意一堆石子中取出任意数量的石子,至少取一颗,至多取出这一堆剩下的所有石子。 结论 :对于一个局面,当且仅当 a[1] xor a[2] xor ...xor a[n]=0 时,该局面为 P 局面,即必败局面。 证明 :二进制位证明即可。 Moore’s Nim 问题 :n 堆石子,每次从不超过 k 堆中取任意多个石子,最后不能取的人失败。 结论 : 这是一个 nim 游戏的变形:把 n 堆石子的石子数用二进制表示,统计每个二进制位上 1 的个数,若每一位上 1 的个数 mod(k+1) 全部为 0 ,则必败,否则必胜。 ( 先手 ) 证明 :分类讨论 N/P 状态。 Staircase N im 问题 : 在阶梯上进行,每层有若干个石子,每次可以选择任意层的任意个石子将其移动到该层的下一层。最后不能操作的人输。 结论 :在 奇数堆的石子做 Nim 。 证明 : 阶梯博弈经过转换可以变为 Nim. 把所有奇数阶梯看成 N 堆石子做 nim 。把石子从奇数堆移动到偶数堆可以理解为拿走石子,就相当于几个奇数堆的石子在做 Nim 。 New Nim 问题 : 在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴

2019.11.09题解

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-04 00:18:25
写在前面: 出题人为啥三道题一个中文两个英文感觉怪怪的 A. 合并集合 标签: 区间Dp 题解: 区间Dp的裸题,刚开考的时候有点紧张没想到Dp,打了一个假的贪心,后来想到Dp的时候觉得我打贪心就是个傻子 f[i][j]代表处理完[i,j]的最大收益: f[i][j]=max{f[i][k]+f[k+1][j]+g[i][k]*g[k+1][j]} 其中g[i][j]代表[i,j]的不同元素个数,$ O(n^2) $预处理即可,总复杂度$ O(n^3) $ B. climb 标签: 二分+贪心 题解: 首先在 可行范围内 二分结束天数(可行范围指的是不会淹死且药品的增量为正,显然在这段区间内是单调的,而其他区间不会对答案产生贡献) 之后考虑枚举最后一天吃的药品,但是有一个问题:我们无法保证这个药片在最后吃时小D没有淹死, 所以继续二分出最大的能选的增量,之后就可以枚举并且O(1)查询啦 C. coin 标签: 博弈论之SG函数 题解: 30pts:考虑把行和列看成点,棋子看成边,边的权值就是是否为反,之后跑类似二分图染色的dfs,原理一样,只是相邻的颜色限制不同 100pts:只考虑有解的情况:对于每个联通块求出SG函数值,按奇偶分为 1>奇-偶/偶-奇 SG=2 2>奇-奇 SG=1 4>偶-偶 SG=0 最后把SG全部^即可 来源: https://www.cnblogs

博弈论嘻嘻

天大地大妈咪最大 提交于 2019-12-03 20:16:20
https://www.cnblogs.com/Simon-X/p/5905960.html 这个介绍得很好,生动形象得介绍了sg。 http://hihocoder.com/contest/hiho46/problem/1 这个呢就是官方点得解释,emmm我选择上面那个 chess hdu 5724 题意:有一个n行20列的棋盘,棋盘上分布着一些棋子,A、B两人轮流下棋,A先手,每次操作可以将某个棋子放到自己右边的第一个空位(也就是说右边如果已经有子,可以跳过它,没有就右移一步),但最多20列,绝对不能超过棋盘,无棋可走的输。 题解:进行状态压缩,bit来表示在一行中一个点有没有棋子,有棋子为1,没有棋子为0,0到(2^20-1)就代表全了所有的可能。 #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int SG[(1<<20)+100],book[30]; void get_SG() { for(int i=0;i<(1<<20);i++) { memset(book,-1,sizeof(book)); int last=-1; for(int j=0;j<20;j++) { if(!((i>>j)&1)) ///空格在最右的位置 last=j; if((i>>j)&1) /

SG函数学习笔记

吃可爱长大的小学妹 提交于 2019-12-03 14:11:21
概念 一.必胜态与必败态 必胜态(N),最优策略下谁面临此状态谁必赢。 必败态(P),最优策略下谁面临此状态谁必输。 二.SG函数 mex运算:定义为求最小的不属于此集合的非负整数。 SG(x)中x表示状态。x是必败态当且仅当SG(x) = 0; 令 S 为所有x走一步可以到达的状态(必须合法)。那么有$SG(x) = mex(SG(y))(y ∈ S) $ 可以暴力求骗分,主要是打表找规律 三.SG定理 游戏和的 SG 函数就是所有子游戏的 SG 函数的异或和,其中所有子游戏互相独立。 所以先手必胜当且仅当每堆石子的 SG 函数的异或和不为 0。 例题 取火柴游戏 分析 由SG定理判断初始状态,设SG = a1^a2^a3^...^an.如果SG == 0就lose 如果必胜,假设是a1中某些被取走,就令a1变成a'。有a'^a2^a3^...^an = 0 = SG ^ SG = SG^a1^a2^a3^...^an; 所以SG^a1 = a'.且必有a1 > a'; 所以一旦有SG^ai < ai,就是在第i堆取走ai - SG^ai个。 #include<iostream> #include<cstdio> #include<cmath> #include<queue> #include<cstring> #include<algorithm> #define lson x

sg

匿名 (未验证) 提交于 2019-12-02 23:52:01
https://scut.online/p/93 每次取走的石子是b的幂次。打表暴力。 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=1000005; //f[i]:可改变i状态的方式 //SG[]:0~n的SG函数值 //S[]:为x后继状态的集合 vector<int> f[MAXN]; int SG[MAXN],S[MAXN]; void getSG(int n){ for(int i = 1; i <= n; i++){ int l=f[i].size(); //后继状态 最多有l 种 for(int j=0;j<=l;j++){ S[j]=0; } for(auto vi:f[i]){ //vi:从i状态能取走的石子数 S[SG[i-vi]]=1; } for(int j=0;j<=l;j++){ if(!S[j]){ SG[i] = j; break; } } cout<<"SG["<<i<<"]="<<SG[i]<<endl; } } int N=120; void enque(int id){ ll cur=1,b=8; while(id>=cur){ f[id].push_back(cur); cur*=b; } } int main() {

【博弈论】

独自空忆成欢 提交于 2019-12-02 23:47:48
博弈论 base 公平组合游戏 无偏博弈:任意局势游戏双方都是平等的回合制双人游戏,平等的含义时当前的所有可行的走法仅仅只依赖当前的局势,与当前谁移动无关 游戏有两个人参与,二者轮流做出决策,双方均知道游戏的完整信息; 任意一个游戏者在某一确定状态可以作出的决策集合只与当前的状态有关,而与游戏者无关; 游戏中的同一个状态不可能多次抵达,游戏以玩家无法行动为结束,且游戏一定会在有限步后以非平局结束 必胜点&必败点 \(P\) 点:必败点,在双方都聪明无比的情况下, 当前 先手必败的位置 \(N\) 点:必胜点,在双方都聪明无比的情况下,当前先手必胜的位置 几个性质: 所有的终止位置都是必败点 \(P\) 从任何一个必胜点 \(N\) 操作,至少有一种方法可以到达一个必败点 \(P\) 从一个必败点 \(P\) 出发,只能到达一个必胜点 \(N\) 常见模型: 巴什博奕 \((Bash GameBash Game)\) 威佐夫博弈 \((Wythoff GameWythoff Game)\) 尼姆游戏$(Nim GameNim Game) $ \(Anti−SG\) 游戏 \(Multi−SG\) 游戏 \(Every−SG\) 游戏 翻硬币游戏 树上删边游戏 博弈图和状态 如果将每个状态视为一个节点 再从每个状态向它的后继状态连边 就可得到一个博弈状态图 定义 必胜状态 为

Day1T1仓鼠的石子游戏——博弈论

两盒软妹~` 提交于 2019-12-02 20:19:00
打比赛的时候还没学博弈论,打完下来花了半个多小时学完,发现这题就是一道 \(SG\) 函数 其实当时差一点就 \(YY\) 出了答案,但是后面太难想,所以没整出来 机房大佬们都说自己没学博弈论,但是都AC 题解 先假装大家都懂 \(SG\) 函数和 \(NIM\) 游戏,一会儿后面再讲 假设先手兔子(我)放的是黑棋,仓鼠(小埋)放的是白棋 首先这道题的 \(n\) 个环可以认为是 \(n\) 个独立的 \(G_1,G_2,G_3...\) 有向图游戏,共同构成 \(G\) 游戏 那么$SG(G) = SG(G_1) $ \(XOR\) \(SG(G_2)\) \(XOR\) \(SG(G_3)......\) 所以我们只需要构造每个环的 \(SG\) 函数即可(本题其实不需要构造 咋构造啊 手玩一下样例,发现当 \(a[i] = 1\) 时有先手必胜态 其他的咋搞呢 好多人手玩样例赌了一下有先手必败态,然后还真的 \(A\) 了,就连题解都是这么写的…… 然而正直的我没…… 咳咳,首先有必败态当且仅当所有的空位置都在黑棋旁边。 那么我们就可以列出所有的必败态 首先空位置一定不能挨在一起,不然就可以填上某个颜色的棋 其次黑棋白棋数量相差一定不超过一(且黑棋多) ,且省略空位后黑棋白棋一定交替轮流出现,因为假如有两个相同颜色的棋相邻的话,其间必有一个空位,而这个空位可以填另一种颜色的棋

CF Gym 102059I Game on Plane(sg函数)

若如初见. 提交于 2019-12-01 13:49:37
链接: https://codeforces.com/gym/102059/problem/I 题意:给定N个点,围成一个圈,每次玩家选择两个点连线,不得与之前连的线相交。 如果玩家连线形成了一个多边形或者没有选的点,输。 题解:连一条边会划分成两个圈(子问题),跑sg函数 #include <bits/stdc++.h> using namespace std; const int maxn=5005; int sg[maxn], vis[maxn]; void calsg() { sg[0]=0; sg[1]=0; sg[2]=1; for(int i=3; i<maxn; i++) { memset(vis, 0, sizeof(vis)); for(int j=0; j<=i-2; j++) vis[sg[j]^sg[i-j-2]]=1; for(int j=0; ; j++) if(!vis[j]){ sg[i]=j; break; } } } int main() { calsg(); int T; for(cin>>T; T--; ) { int n; cin>>n; printf(sg[n]?"First\n":"Second\n"); } return 0; } View Code 来源: https://www.cnblogs.com/Yokel062/p

POJ 2311 Cutting Game(二维sg)

我是研究僧i 提交于 2019-12-01 13:27:36
链接: https://vjudge.net/problem/POJ-2311 题意:给出一个N*M的纸片,每一次可以把一部分剪成两部分,谁剪出1*1的就赢了。 题解:二维sg函数,每一个变量可以随便转但是都不会先剪出1*n或者n*1,这样就必败了,直接让状态的后继中最小的边是2即可 #include <cstdio> #include <iostream> #include <cstring> using namespace std; int sg[205][205]; int vis[520]; int dfs_sg(int n, int m) { if(sg[n][m]!=-1) return sg[n][m]; memset(vis, 0, sizeof(vis)); for(int i=2; i<=n-i; i++) //sg[1][k] 和 sg[k][1]=0, 所以都不会这样的后继状态 vis[dfs_sg(i, m)^dfs_sg(n-i, m)]=1; for(int i=2; i<=m-i; i++) //二维的sg有2个方向可以转 vis[dfs_sg(n, i)^dfs_sg(n, m-i)]=1; for(int i=0; ; i++) if(!vis[i]) return sg[n][m]=i; } int main() { //freopen("in