A. 【例题1】取火柴游戏
解题思路
》》洛谷link
NIM游戏模板 主要是输出方案
必胜情况下 必有
a
i
x
o
r
x
<
x
a_i~xor~x<x
ai xor x<x 找到
a
i
a_i
ai 并输出即可
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#define ll long long
#define ldb long double
using namespace std;
int k,a[500010],tmp,ok,flag;
int main(){
scanf("%d",&k);
for(int i=1;i<=k;i++)
{
scanf("%d",&a[i]);
flag^=a[i];
}
if(flag)
{
for(int i=1;i<=k;i++)
{
if((a[i]^flag)<a[i])
{
tmp=a[i]-(a[i]^flag),a[i]-=tmp,ok=i;
break;
}
}
printf("%d %d\n",tmp,ok);
for(int i=1;i<=k;i++)
printf("%d ",a[i]);
}
else printf("lose\n");
}
B. 【例题2】数字游戏
解题思路
这要给对手留下1,这个人就是必胜的,分类讨论:
- 当n=1时,显然先手必败
- 当n=2时,显然先手必胜
- 当n为奇数时,可以直接除以n,给对手留下1,先手必胜
- 当n=2^x,x不为0时,n没有大于1的奇数因子,先手只能将n减1,n变成了奇数,对手再除以n,给你留下1,先手必败。
- 当n=2*W时,如果W是质因子的话,显然先手必败,否则,先手必胜(因为我们可以把W除掉一个因数变成质数)。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#define ll long long
#define ldb long double
using namespace std;
int T,n;
int main() {
scanf("%d",&T);
for(int i=1; i<=T; i++) {
scanf("%d",&n);
if(n==1) {
printf("FastestFinger\n");
continue;
}
if(n==2) {
printf("Ashishgup\n");
continue;
}
if(n%2!=0) {
printf("Ashishgup\n");
continue;
}
if(n%2==0) {
bool flag=0;
for(int i=30; i>0; i--)
if(1<<i==n) {
flag=1;
break;
}
if(flag) {
printf("FastestFinger\n");
continue;
}
for(int i=3; i*i<=n; i++)
if(n%i==0) {
flag=1;
break;
}
if(flag)
printf("Ashishgup\n");
else printf("FastestFinger\n");
}
}
}
C. 【例题3】魔法珠
解题思路
标准S G 游戏
枚举约数 然后记忆化搜索
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<cmath>
#define ll long long
using namespace std;
int n,a[1100],sg[1010];
int SG(int n)
{
if(sg[n]!=-1)return sg[n];
int k=0;
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
if(n>i)k^=SG(i);
if(i*i!=n&&n>n/i)
k^=SG(n/i);
}
}
bool flag[110];
memset(flag,0,sizeof(flag));
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
if(n>i)flag[k^SG(i)]=1;
if(i*i!=n&&n>n/i)
flag[k^SG(n/i)]=1;
}
}
int ans=0;
while(flag[ans])ans++;
return sg[n]=ans;
}
int main(){
while(scanf("%d",&n)!=EOF){
memset(sg,-1,sizeof(sg));
sg[0]=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int ans=0;
for(int i=1;i<=n;i++)ans=ans^SG(a[i]);
if(ans)printf("freda\n");
else printf("rainbow\n");
}
}
D. 【例题4】剪纸游戏
解题思路
1
×
1
1×1
1×1由
1
×
n
,
n
×
1
,
2
×
2
,
2
×
3
,
3
×
2
1 × n,n × 1 , 2 × 2 , 2 × 3 , 3 × 2
1×n,n×1,2×2,2×3,3×2 几个状态得到
前
2
2
2种是必胜状态 后面的是必败状态
然后就在 S G S G SG函数里枚举分割的两块剪纸
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#define ll long long
#define ldb long double
using namespace std;
const int N=1100;
int n,m,sg[1100][1100];
int SG(int n,int m)
{
if(sg[n][m]!=-1)return sg[n][m];
bool flag[2*N];
memset(flag,0,sizeof(flag));
for(int i=2;i*2<=n;i++)
flag[SG(i,m)^SG(n-i,m)]=1;
for(int i=2;i*2<=m;i++)
flag[SG(n,i)^SG(n,m-i)]=1;
int ans=0;
while(flag[ans])ans++;
return sg[n][m]=sg[m][n]=ans;
}
int main(){
memset(sg,-1,sizeof(sg));
sg[2][2]=sg[2][3]=sg[3][2]=0;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(SG(n,m))
printf("WIN\n");
else printf("LOSE\n");
}
}