《博弈论》
尼姆博弈:
一:给定n堆石子,每次可以从一堆中取任意数量个,最后一个取完的获胜。
二:给定n堆石子,每次可以从一堆中取最多k个,最后一个取完的获胜。
三:给定n堆石子,每次可以选最多k堆,每堆可以取任意数量个,最后一个取完的获胜。
Solution:
一:这是最经典的一种尼姆博弈,先手必胜态为所有堆石子数异或后答案不为0.否则为必败态。
二: 对每堆的石子数模上(k + 1),就变成了经典的尼姆博弈类型。
三:这个问题又被称为nimk博弈,我们把每堆石头都表示成二进制,从每个二进制位去考虑。
统计每个二进制位上1的个数,如果满足每个二进制位上1的个数都可以 % (k + 1) = 0.那么先手必败。
反之,先手必胜。
巴什博弈:
有一堆n个石子,每次最多可以取k个,最后一个取完的获胜。
Solution:
如果n % (k + 1) == 0,那么后手只要每次和先手的数量凑成k + 1即可,那么后手必胜。
反之,先手先加上取模后的余数,然后就满足了 n % (k + 1) == 0的情况,此时先手变后手了,那么先手必胜。
威佐夫博弈:
给出两堆石子,有两种操作:1:从一堆中取任意个石子。2:从两堆中取相同数量的石子。最后一个取完的获胜。
结论:
黄金分割比:1.618 = (sqrt(5)+ 1) / 2 。//精度高
设两堆石子数为a,b。如果满足a = (a - b) * 1.618,先手必败,反之必胜。
https://www.cnblogs.com/csushl/p/10331498.html
HDU1079:Calendar Game
可以发现一共的日期并不多,所以考虑dp转移必胜态即可。其实这里也有规律可以找规律。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 1e3 + 5; const double eps = 1e-10; const LL Mod = 998244353; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { int f = 1;LL x = 0;char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } inline long long ADD(long long x,long long y) {return (x + y) % Mod;} inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;} inline long long MUL(long long x,long long y) {return x * y % Mod;} bool dp[105][15][35]; int a[15]; int cal(int year) { if(year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) return 29; return 28; } void init() { a[1] = 31,a[2] = 0,a[3] = 31,a[4] = 30,a[5] = 31,a[6] = 30,a[7] = 31,a[8] = 31,a[9] = 30,a[10] = 31,a[11] = 30,a[12] = 31; dp[101][11][3] = 1; dp[101][11][2] = 0; dp[101][11][1] = 1; for(int i = 2001;i >= 1900;--i) { int L = 1,r = 12; if(i == 2001) r = 10; for(int j = r;j >= L;--j) { int ll = 1,rr = a[j]; if(j == 2) rr = cal(i); for(int k = rr;k >= ll;--k) { int tag = 1; if(k != rr) tag &= dp[i - 1900][j][k + 1]; else { if(j == 12) tag &= dp[i - 1900 + 1][1][1]; else tag &= dp[i - 1900][j + 1][1]; } if(i == 2001 && j == 10 && k >= 5) tag &= 1; else { if(j == 12) tag &= dp[i - 1900 + 1][1][k]; else { if(j == 1) { int nxt = cal(i); if(nxt >= k) tag &= dp[i - 1900][j + 1][k]; } else { if(a[j + 1] >= k) tag &= dp[i - 1900][j + 1][k]; } } } if(tag == 0) dp[i - 1900][j][k] = 1; } } } } void solve() { int x,y,z;scanf("%d %d %d",&x,&y,&z); printf("%s\n",dp[x - 1900][y][z] ? "YES" : "NO"); } int main() { init(); int ca;scanf("%d",&ca); while(ca--) { solve(); } system("pause"); return 0; }
HDU1525:Euclid's Game
我们可以发现对于只能减去一倍的情况,是没有什么好说的只能老实去减。
然后就是对于多倍的情况,可以发现的是,有两个种操作,一种全部减去,先手变后手,另一种减去k - 1倍,那么下一个人只能减去剩下的一倍,先手依旧变先手。
所以对于多倍我们可以操作成对自己有利的情况,那么,很显然能决定答案的,就是最后一个多倍的情况。也就是说谁能先操作最后一个多倍,那么谁就能赢。
然后因为对于多倍我们可以决定下一次的先后手情况,那么很显然就说明第一个操作多倍的人,可以让自己也先操作下一个多倍。
那么我们就可以知道了,谁先操作第一个多倍,谁就可以永远第一个操作所有的多倍,也就可以获胜。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 1e3 + 5; const double eps = 1e-10; const LL Mod = 998244353; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { int f = 1;LL x = 0;char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } inline long long ADD(long long x,long long y) {return (x + y) % Mod;} inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;} inline long long MUL(long long x,long long y) {return x * y % Mod;} void solve() { int x,y; while(scanf("%d %d",&x,&y),x != 0 || y != 0) { int ta = 1; if(x < y) swap(x,y); if(x % y == 0) {printf("Stan wins\n");continue;} int g = 0; while(1) { if(x < y) swap(x,y); if(x % y == 0) {printf("%s\n",g == 0 ? "Stan wins" : "Ollie wins");break;} int k = x / y; if(k == 1) x -= y,g ^= 1; else { if(g == 0) {printf("Stan wins\n");break;} else {printf("Ollie wins\n");break;} } } } } int main() { solve(); system("pause"); return 0; }
HDU1564:Play a game
猜测了一下只和总个数有关,结果就是这样。
模拟了一下发现,不管怎么样先后手想去改变输赢情况,即棋子奇偶数,隔出去的数量总是偶数个,不会改变原来的奇偶情况。
所以只和原来的奇偶数有关。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 1e3 + 5; const double eps = 1e-10; const LL Mod = 998244353; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { int f = 1;LL x = 0;char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } inline long long ADD(long long x,long long y) {return (x + y) % Mod;} inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;} inline long long MUL(long long x,long long y) {return x * y % Mod;} void solve() { int n; while(scanf("%d",&n),n != 0) { printf("%s\n",n % 2 == 0 ? "8600" : "ailyanlu"); } } int main() { solve(); system("pause"); return 0; }
HDU1846:Brave Game
巴什博弈的模型。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 1e3 + 5; const double eps = 1e-10; const LL Mod = 998244353; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { int f = 1;LL x = 0;char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } inline long long ADD(long long x,long long y) {return (x + y) % Mod;} inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;} inline long long MUL(long long x,long long y) {return x * y % Mod;} void solve() { int n,m;scanf("%d %d",&n,&m); if(m >= n) printf("first\n"); else { if(n % (m + 1) == 0) printf("second\n"); else printf("first\n"); } } int main() { int ca;scanf("%d",&ca); while(ca--) { solve(); } system("pause"); return 0; }
HDU1847:Good Luck in CET-4 Everybody!
SG板子题。SG函数求一下即可。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> pii; const int N = 1e4 + 5; const int M = 1e3 + 5; const double eps = 1e-10; const LL Mod = 998244353; #define pi acos(-1) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { int f = 1;LL x = 0;char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } inline long long ADD(long long x,long long y) {return (x + y) % Mod;} inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;} inline long long MUL(long long x,long long y) {return x * y % Mod;} bool S[N]; int SG[N]; int f[10]; void init(int x) { memset(SG,0,sizeof(SG)); for(int i = 1;i <= x;++i) { memset(S,0,sizeof(S)); for(int j = 0;j <= 9 && f[j] <= i;++j) S[SG[i - f[j]]] = 1; for(int j = 0;;++j) if(!S[j]) {SG[i] = j;break;} } } void solve() { int n; f[0] = 1; for(int i = 1;i <= 9;++i) f[i] = f[i - 1] * 2; while(~scanf("%d",&n)) { init(n); printf("%s\n",SG[n] == 0 ? "Cici" : "Kiki"); } } int main() { solve(); system("pause"); return 0; }