P4018 Roy&October之取石子
#规律 #数学
数字有这几种情况
一、2、3、5都是质数;
二、6是第一个两个质数的幂之和。
三、6的倍数 6*n,一定不是单个质数的幂次方。因为除2外的质数都是奇数,奇数的乘积是奇数。对于2,因为6*n包含因子3,所以 6*n不是2的幂
四、对于所有6的倍数。先手无法取到6,每次都由后手补到6。如此循环直至取完,后手必胜。
五、对于所有非6的倍数,先手可以取1-5个将其化为6的倍数,转为四。
所以6的倍数,先手必败 。
石子游戏
#必胜态和必败态
G-石子游戏_第四届上海理工大学程序设计全国挑战赛 (nowcoder.com)
刚开始暴力打表,没发现规律
int dfs(int k,int l)
{
if(k>=l) return 0; //必败态
for(int i=1;i<=k;i++)
{
if(k+i<=l&&dfs(k+i,l)==0)
return 1; //下一个人的状态是必败态
}
return 0;
}
打完后一周,朋友提供了思路,恍然大悟
倒 推
终态:now>=k
必胜态:ceil(k/2)<now<k
必败态:now=ceil(k/2)-1
所以每次先手添加石子,把状态推到必败态,就能赢
递归每一次的必败态,看先手能否把状态推到必败态送给后手
void solve()
{
double n,k;
cin>>n>>k;
if(n==k) {cout<<"Bob"<<endl;return;}
while(n<k)
{
if(n>=ceil(k/2)) //先手的初始状态能达到第一次的必败态
{
cout<<"Alice"<<endl;
return;
}
else k=ceil(k/2)-1; //继续递归寻找第一次的必败态
}
cout<<"Bob"<<endl; //先手的初始状态不能达到第一次的必败态
return;
}
Alice and Bob
A-Alice and Bob_2024牛客五一集训派对day4 (nowcoder.com)
这题是队友做的,赛后队友讲了一下思路
打 表
首先把一次能取完的情况标记,接着对必败态上的必胜态进行标记
int main(){
for(int i=0;i<=5000;i++)
for(int j=0;j<=5000;j++){
if(!win[i][j]){
for(int k=1;i+k<=5000;k++)
for(int s=0;j+k*s<=5000;s++)
win[i+k][j+k*s]=1;
for(int k=1;j+k<=5000;k++)
for(int s=0;i+k*s<=5000;s++)
win[i+s*k][j+k]=1;
}
}
cin>>t;
while(t--){
cin>>n>>m;
if(win[n][m]) cout<<"Alice"<<endl;
else cout<<"Bob"<<endl;
}
return 0;
}
P3150 pb的游戏(1)
P3150 pb的游戏(1) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
偶数则先手必胜
如果是奇数,只能分割为奇数和偶数。无论先手如何拆解,后手如果能得到奇数,就将奇数分解为偶数+1,把作为必败态的偶数给先手。
欧几里得的游戏
P1290 欧几里德的游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
bool sg(int x,int y)
{
if(!y) return 1;
if(x/y==1)
{
return !sg(y,x%y);
}
return 1;
}
void solve()
{
int a,b,c,d;
cin>>a>>b;
c=max(a,b);
d=min(a,b);
a=c,b=d;
if(sg(a,b)) cout<<"Stan wins"<<endl;
else cout<<"Ollie wins"<<endl;
}
P2953 [USACO09OPEN] Cow Digit Game S
P2953 [USACO09OPEN] Cow Digit Game S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
清楚概念
必胜态:后继状态存在必败态
必败态:后继状态全为必胜态
利用这个性质打表
int N=1e6+10;
int fmax(int x)
{
int m=0;
while (x) {if (x%10>m) m=x%10; x/=10;}
return m;
}
int fmin(int x)
{
int m=10;
while (x) {if (x%10<m&&x%10) m=x%10; x/=10;}
return m;
}
bool sg[1000010];
void solve()
{
for(int i=1;i<=9;i++) sg[i]=true;
for(int i=10;i<=1000000;i++)
{
if(sg[i-fmax(i)]&&sg[i-fmin(i)]) sg[i]=false;
else sg[i]=true;
}
int q;
cin>>q;
while(q--)
{
int n;
cin>>n;
if(sg[n]) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
Alice Game
sg函数打表
void get_sg()
{
int k;
cin >> k;
sg[0] = 0;
for(int i = 1; i <= k; i++) sg[i] = 1;
for(int i = k + 1; i <= 100; i++)// 打表
{
set<int> s;
for(int left = 1; left < i; left++)//枚举左边有多少个
{
int right = i - k - left;
if(left > 0 && right > 0) s.insert(sg[left] ^ sg[right]);
}
for(int mex=0;;mex++)
{
if(!s.count(mex)) sg[i]=mex;
}
}
for(int i = 0; i <= 100; i++)
{
cout << sg[i] << " ";
}
}
C. Battle
Problem - 104385C - Codeforces
sg函数打表
bool sg(int x)
{
if(f[x]!=-1) return f[x];
set<int>s;
for(int i=1;i<10;i++)
{
if(x>=pow(p,i)) s.insert(sg(x-pow(p,i)));
}
for(int i=0;;i++)
{
if(!s.count(i))
return f[x]=i;
}
}
#include <bits/stdc++.h>
using namespace std;
const int N = 110,M=3e5+10;
typedef long long ll;
ll x,p,n,res;
ll sg1(ll x)//x为奇数的情况
{
return x%2;
}
ll sg2(ll x)//x为偶数的情况
{
if(x==p) return 2;
if(x%2==0) return 0;
else return 1;
}
int main()
{
res=0;
cin>>n>>p;
for(ll i=0;i<n;i++){
cin>>x;
if(p&1)
{
res^=sg1(x);
}
else
{
x%=(p+1);
res^=sg2(x);
}
}
if(res) cout<<"GOOD"<<'\n';
else cout<<"BAD"<<'\n';
return 0;
}
Distinct Numbers
C - Distinct Numbers (atcoder.jp)
假设有n个数,那么这个游戏的终态就是0、1、2、3……n-1