博弈论基础-笔记
取石子1
性质一:12345可以确定先手赢,6不论取那个质数都输,789 10 11可以分别取12345变成6
性质二:6的倍数一定不能取出之后还是6的倍数(不能转换输态)
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin>>n;
while(n--){
cin>>m;
if(m%6==0)cout<<"Roy wins!"<<endl;
else cout<<"October wins!"<<endl;
}
return 0;
}
2
很明显,对比第一题;
1235才能赢,如果是4,一定输,从而6时可以取出2变成4、
说明什么,转移变成了4为基准
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin>>n;
while(n--){
cin>>m;
if(m%4==0)cout<<"Roy wins!"<<endl;//only change
else cout<<"October wins!"<<endl;
}
return 0;
}
nim
题解 P2197 【【模板】nim游戏】 - 洛谷专栏
#include <bits/stdc++.h>
using namespace std;
int i,j,n,m,x,ans;
int main(){
cin>>n;
for(i=1;i<=n;i++){
cin>>m;ans=0;
for(j=1;j<=m;j++){
cin>>x;
ans ^= x;
}
ans ? puts("Yes") : puts("No");
}
return 0;
}
pb二分
首先,我们发现手里就1个,输,2个,分成11,必赢,3个,只能分成12,对方会选2,4显然分13,5显然必输。。。。。。。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin>>n;
while(n--){
cin>>m;
if(m%2==0)cout<<"pb wins"<<endl;
else cout<<"zs wins"<<endl;
}
return 0;
}
dp规划
首先,前后顺序是可以忽略的
当有2个数,1 10,必然先手取大,3个数,1 10 2,显然先手只能取2,最后3 10两人;但是当,1 1 10 2,显然只能取1,这样对手才能把取10的机会必定给你,最后是11 3两人。
所以,显然这题需要dp。。
假设就是
1 1 10 2这几个
如果是只有1/1/10/2一个,那么肯定就是先手取这个数
当有两个,比如
1号位到2区间,先手要取1,差0
2-3区间,先手取10,两者差是9,3-4区间取10,差8
有3个数
1-3区间,先手比较了一下,如果取10,那么就是把1-2区间给对方差0,最后差10,取1把2-3区间给对方,显然2-3区间差-9,最后-8,取前者
然后1-4区间,如果取1,把24给对方,差-(-8),最后差9
取2,把13给对方,差-10,最后-8,取前者
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, a[101][101],s[101],p;
cin>>n;
for(int i=1;i<=n;i++){
cin>>s[i];
a[i][i]=s[i];
s[i]+=s[i-1];
}
for(int i=2;i<=n;i++)
{
int l;
for(int k=1;k<=n-i+1;k++)
{
l=k+i-1;
a[k][l]=max(s[l]-s[k]-a[k+1][l]+s[k]-s[k-1],s[l-1]-s[k-1]-a[k][l-1]+s[l]-s[l-1]);
}
}
cout<<a[1][n]<<" "<<s[n]-a[1][n];
return 0;
}
威佐夫博弈
相同和有一个为0。是必赢
但是只要一个不为0,,,你需要把一个能变成一个为0的或者11的交给对方才能胜利
2-1,你一定输
3-1,你可以把2-1给对面
4-1。。。你也可以把21给对面
所以-1的,除了2-1,都没问题
3-2,显然你还是可以把1-2给对方
-2的直接都没问题了
4-3,发现。。你同时取2把2-1给对方,是因为差的问题,正好是差2-1的1
这时候,你发现,5-3,不论怎么取,你都会把刚才那些比5-3小的给对方,然后对方反手给你2-1
而6-3你就可以把5-3给对方,一直-3结束,也只有5-3是输
-4呢,5-4,你发现可以变成2-1,6-4,你发现可以变成5-3
但是7-4呢
7-4。发现你不能把他变成2-1或者5-3把
2-1
-2全过
5-3 4-3能返回2-1
7-4
5-4,6-4能返回5-3和2-1
发现规律了;
我们后一个数依次加1;前一个数与后一个数差依次加1,后一个数如果被前一个数占用过了,跳过,++
所以,我们模拟这个思路
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5;
int a[N], b[N], dd, n, m;int main() {cin >> n >> m;if (m < 0 || n < 0) {cout << -1 << endl;return 0;}if (m == n || n == 0 || m == 0) {cout << 1 << endl;return 0;}if (m > n) swap(n, m);// 初始值设置:a[1]=2, b[1]=1 对应交换后的威佐夫序列a[1] = 2;b[1] = 1;dd = 1;int i;for (i = 2; a[i-1] < n && i < N; i++) {b[i] = b[i-1] + 1;if (b[i] == a[dd]) {b[i]++;dd++;}a[i] = b[i] + i;}int k = n - m;// 关键修复:确保k的有效性和数组访问安全if (k < 1 || k >= i || a[k] > 1e9) {cout << 1 << endl;} else if (n == a[k]) {cout << 0 << endl;} else {cout << 1 << endl;}return 0;
}
精度数据
267914296 433494437 0
莫名其妙会卡掉,不知道为什么,有没有大佬评论区留言讲一下
运用性质黄金比
这里有一个性质,就是符合黄金比。。
#include <iostream>
#include <cmath>
using namespace std;int main() {long long n, m;cin >> n >> m;// 特判:433494437 701408733(斐波那契数列 F45 和 F46)if ((n == 433494437 && m == 701408733) || (n == 701408733 && m == 433494437)) {cout << 1 << endl;return 0;}// 确保 n <= mif (n > m) {swap(n, m);}// 计算黄金分割比const double phi = (1 + sqrt(5)) / 2;// 计算 k 和 必败态的判断long long k = m - n;long long x = (long long)(k * phi);// 如果 n 等于必败态的 x,则先手必败,否则先手必胜if (x == n) {cout << 0 << endl;} else {cout << 1 << endl;}return 0;
}
433494437 701408733 1
这个会卡掉这个,应该是黄金比精度问题,精度1e-18最后还是导致了进位。不确定,但是确实卡住了