博弈论简单算法

一、巴什游戏

Problem - 1846 (hdu.edu.cn)

题目简述:有n颗石子,甲先取,乙后取,每次可拿1-m颗石子,轮流拿下去,拿到最后一颗的人获胜。

结论是若n%(m+1) == 0则先手败,否则先手胜。

先想当有m+1个石子时候不论先手取多少个 后手一定可以一次性取完,所以后手一定赢。

那么再思考如果有n*(m+1)个,先手取k个,则后手取m+1-k个保证每一个周期结束后数目仍然是m+1的倍数。

直到剩m+1个的时候,先手取完后,后手全部取完。

如果不是m+1的倍数那么先手只需要一定数目的石子让其变为m+1的倍数即可 所以有当n%(m+1)==0先手必败n%(m+1) != 0先手必胜

二、Nim游戏

P2197 【模板】Nim 游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

尼姆游戏(Nim Game)是由n堆石子,数量分别是{a1,a2,…,an},两个玩家轮流拿石子,每次可以从任意一堆拿走任意数量的石子,拿到最后一个石子的玩家获胜。
结论是若a1⊕a2⊕…an≠0,则先手必胜(N),否则先手必败(P)

三、SG函数

1.mex函数

mex(a1, a2, a3, ... ... , an) 是指 >=0 的数中最小的 没有出现在 a1, a2, a3, ... ... , an 中的数,例如:mex(0, 1, 2, 3) = 4,mex(2, 3) = 0,mex(1, 2) = 0

2.SG函数求解巴什游戏

结论是如果sg(x) = 0,则x点为必败点

关键在计算sg函数的值

Problem - 1846 (hdu.edu.cn)

void getSG(){
	memset(sg, 0, sizeof sg);
	for(int i = 0; i <= n; i++){
		memset(s, 0, sizeof s);
		//把i的后继点放到集合s中
		for(int j = 1; j <= m && i - j >= 0; j++){
			s[sg[i - j]] = 1;
		}
		//计算sg[i];
		for(int j = 0; j <= n; j++){
			if(!s[j]){
				sg[i] = j;
				break;
			}
		}
	}
}

void solve() {
	cin >> n >> m;
	getSG();
	if(sg[n]) cout << "first\n";
	else cout << "second\n";
}

3.SG函数求解Nim游戏

结论是计算每一堆石子的sg函数值,再全都异或起来

一般会在取石子个数有特殊规则时使用。

Not a Nim Problem - Codeforces

#include<bits/stdc++.h>
using namespace std;
#define qio ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
typedef long long ll;
typedef double db;

const int N = 3e5 + 10;
const int M = 1e7 + 10;
int a[N], sg[M];

void init(){
	sg[1] = 1;
	sg[2] = 0;
	int cnt = 1;
	for(int i = 3; i <= M; i++){
		if(sg[i] || i % 2 == 0) continue;
		cnt++;
		sg[i] = cnt;
		for(int j = 1; i * j <= M; j++){
			if(sg[i * j] == 0 && (i * j) % 2 == 1) sg[i * j] = cnt;
		}
	}
}

void solve() {
	int n; cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	int ans = 0;
	for(int i = 1; i <= n; i++){
		ans ^= sg[a[i]];
	}
	if(ans){
		cout << "Alice\n";
	}else{
		cout << "Bob\n";
	}
}

signed main() {
	qio
	int T = 1;
	init();
	cin >> T;
	while (T--)solve();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值