五大基础博弈论

五大基础博弈论

Nim 博弈

【题意】有n堆石子,每堆ai个石头,每个人任意可以选取一堆,取走任意多个,可以把一堆取光,但是不能不取,取走最后一个的人获胜。

【题解】a1^a2^……^an==0 先手必输

# include <bits/stdc++.h>
using namespace std;

const int MAXN=1e4+100;
int a[MAXN];

int main()
{
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
int flag=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
flag=flag^a[i];
}
if(flag) printf("First win\n");
else printf("Second win\n");
}

return 0;
}  

在先手会赢的情况下输出第一步可以走的方案数

# include <bits/stdc++.h>
using namespace std;

const int MAXN=200;
int a[MAXN];
int main()
{
int M;
while(~scanf("%d",&M)){
int ans=0;
if(M==0) break;
for(int i=1;i<=M;i++){
scanf("%d",&a[i]);
ans=ans^a[i];
}
if(ans==0) printf("0\n");
else{
int sum=0;
for(int i=1;i<=M;i++){
if((ans^a[i])<a[i]){//a[i]-ans^(a[i])为具体方案
sum++;
}
}
printf("%d\n",sum);//输出方案数
}
}


return 0;
}

反nim博弈 取到最后一个的输

# include <bits/stdc++.h>
using namespace std;

int a[50];
int main()
{
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
int ans=0;
int cnt=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
ans=ans^a[i];
if(a[i]>1) cnt++;
}
if(cnt==0){
if(ans) printf("Second win\n");
else printf("First win\n");
}else if(cnt==1){
printf("First win\n");
}else{
if(ans) printf("First win\n");
else printf("Second win\n");
}
}

return 0;
}

nimk —— nim的扩展

【题意】一共有n堆石子,每个人每次可以从不超过k堆中取任意多个石子,最后不能取的人失败

【题解】把n堆石子的石头数用二进制表示,统计每个二进制位上1的个数,若每一位上1的个数mod(k+1)全部位0,则先手必败

# include <bits/stdc++.h>
using namespace std;

const int MAXN=1e4+100;
int a[MAXN];
int len[MAXN];
int main()
{
int T;
scanf("%d",&T);
int cntt=0;
while(T--){
cntt++;
int n,m;
scanf("%d%d",&n,&m);
int maxl=0;
memset(len,0,sizeof(len));
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
int aa=a[i];
int nowl=0;
while(aa){
len[nowl]+=aa%2;
aa=aa/2;
nowl++;
}
maxl=max(maxl,nowl);
}
int flag=0;
for(int i=0;i<=maxl;i++){
if(len[i]%(m+1)){
flag=1;
break;
}
}
if(flag) printf("Case #%d: Alice\n",cntt);
else printf("Case #%d: Bob\n",cntt);
}
return 0;
}

Bash 博弈

【题意】一堆n个物品,每个人每次至少取一个,最多取m个

【题解】n==k*(m+1) 先手必败

n==k*(m+1)+r ( r>=1&&r<=m ) 先手必胜

# include <bits/stdc++.h>
using namespace std;

int main()
{
int C;
scanf("%d",&C);
while(C--){
int n,m;
scanf("%d%d",&n,&m);
if(n%(m+1)==0) printf("Second win\n");
else printf("First win\n");
}

return 0;
}

在先手为必胜态的情况下,输出第一步的方案

# include <bits/stdc++.h>
using namespace std;

int main()
{
int m,n;
while(~scanf("%d%d",&n,&m)){//n为石子总数,m为限制数
if(n%(m+1)==0) printf("none\n");//必败态
else{
if(n>(m+1)){//输出余数,转变为后手的必败态
printf("%d\n",n%(m+1));
}else{
for(int i=n;i<=m;i++){//从总数到限制数,无论怎么取都能够一次性取完,先手必胜
if(i!=n) printf(" ");
printf("%d",i);
}
printf("\n");
}
}
}


return 0;
}

Wythoff 博弈

【题意】有两堆石子,每个人可以从某一堆取或者从两堆中取同样多的物品,每次至少取一个,多则不限

【题解】[ ak , bk ] ( ak <= bk ) ak=(k*(sqrt(5)+1)/2) bk=ak+k 奇异局势 先手必输

# include <bits/stdc++.h>
using namespace std;

int main()
{
int a,b;
while(~scanf("%d%d",&a,&b)){
if(a>b) swap(a,b);
int k=b-a;
int ans=k*(sqrt(5.0)+1.0)/2.0;
if(a==ans) printf("Second win\n");
else printf("First win\n");
}

return 0;
}

输出必胜态下第一次取完之后剩下的两堆石子数 x , y ( x <= y ) ,如果在任意对中取石子、在两堆中同时取石子都能胜,先输出取走相同石子的情况。

# include <bits/stdc++.h>
using namespace std;

int main()
{
int a,b;
while(scanf("%d%d",&a,&b)!=EOF){
if(a==0&&b==0) break;
int k=b-a;
int ans=k*(sqrt(5.0)+1.0)/2.0;
if(ans==a) printf("0\n");
else{
printf("1\n");
for(int i=1;i<=a;i++){//取两堆,枚举取的个数
int a1=a-i,b1=b-i;
if(a1==ans) printf("%d %d\n",a1,b1);
}
for(int i=0;i<=b;i++){//取一堆,枚举差值
int aa=a,bb=b-i;
if(aa>bb) swap(aa,bb);
int kk=bb-aa;
int tmp=kk*(sqrt(5.0)+1.0)/2.0;
if(tmp==aa) printf("%d %d\n",aa,bb);
}
}
}


return 0;
}

Fib 博弈

【题意】一堆n个石子,先手不能在第一次取完所有石子,之后每次可以取的石子数介于1到对手刚取的石子数的两倍之间(包含)

【题解】石子数为斐波那契数则先手必败

# include <bits/stdc++.h>
using namespace std;

int fib[233];
set<int> s;
void init()
{
fib[1]=1,fib[2]=1;
s.insert(1);
for(int i=3;i<47;i++){
fib[i]=fib[i-1]+fib[i-2];
s.insert(fib[i]);
}
return ;
}
int main()
{
int n;
init();
while(~scanf("%d",&n)){
if(n==0) break;
if(s.count(n)) printf("Second win\n");
else printf("First win\n");
}


return 0;
}

环形博弈

【题意】n个石子围成一个环,每次取一个或者取相邻的2个

【题解】石子数<=2先手赢

# include <bits/stdc++.h>
using namespace std;

typedef long long LL;
int main()
{
int T;
scanf("%d",&T);
while(T--){
LL n;
scanf("%lld",&n);
if(n<=2) printf("First win\n");
else printf("Second win\n");
}

return 0;
}

P-N图

打表工具

# include <bits/stdc++.h>
using namespace std;

int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)){
if(n==0&&m==0) break;
char s[20][20];
/*
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
s[i][j]='*';
}
}
*/
s[n][1]='P';
for(int i=n-1;i>=1;i--){
if(s[i+1][1]=='P') s[i][1]='N';
else s[i][1]='P';
}
for(int j=2;j<=m;j++){
if(s[n][j-1]=='P') s[n][j]='N';
else s[n][j]='P';
}

for(int i=n-1;i>=1;i--){
for(int j=2;j<=m;j++){
if(s[i+1][j-1]=='N'&&s[i][j-1]=='N'&&s[i+1][j]=='N') s[i][j]='P';
else s[i][j]='N';
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) printf("%c ",s[i][j]);
printf("\n");
}
}

return 0;
}

SG函数 打表

【题意】 2 个人玩游戏,从 1 开始,轮流对数进行累乘(2~9),直到超过(>=)一个指定的值。

# include <bits/stdc++.h>
using namespace std;

const int MAXN=1e4+100;
int sg[MAXN];
int main()
{
sg[0]=0,sg[1]=0;
for(int i=2;i<MAXN;i++){
set<int> s;
for(int j=2;j<10&&j<=i;j++){
if(i%j==0) s.insert(sg[i/j]);
else s.insert(sg[i/j+1]);
}
int cnt=0;
while(s.count(cnt)) cnt++;
sg[i]=cnt;
}
for(int i=0;i<100;i++) cout<<i<<" "<<sg[i]<<endl;

return 0;
}

bash f-n

当n*m为奇数时,总是可以找到一条路径走偶数步到达终点,先手必败

当n*m为偶数时,总是可以找到一条路径走奇数步到达终点,先手必胜

# include <bits/stdc++.h>
using namespace std;

int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)){
if(n==0&&m==0) break;
if(n*m%2==0) printf("Wonderful!\n");
else printf("What a pity!\n");
}

return 0;
}



posted @   fengzlj  阅读(165)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示