博弈论题集


 博弈论是算法比赛中比较常出的一类题型,觉得掌握的不好,平时也不会写,经常要花费很长时间也写不出来,所以整一个博弈论题集来收录一下那些博弈论的题

尼姆的一切

来源于CF
 这个题刚开始读就出现了错误,导致花费了很长时间去想一个错误的思路,
 题意:A和B玩游戏,有n堆石子,每次拿走每堆石子中的k个,最后一个拿不了的就输,如果都以最优策略玩游戏输出谁会赢。(这里是每堆每次都要拿k个)
如果石头的个数分别是1,2,3,4,5,6.....这种连续的话,根据题意就是直接每次拿一个,拿六次就行了(因为这种情况每次最小的都是1,就只能拿1)
 所以我们又可以想到一点,就是形如1,1,1,2,2,3,3,3,3,4,5的序列就可以看成1,2,3,4,5.每次就直接剪掉了。所以对于这些数字我们可以用去重排序的set来实现。
 还有一点考虑当前面取完,只剩最后一堆时,这时该谁取,这个人只需要把最后一堆全取完,即可获胜,对于每个人,最好的方案都是让自己在剩最后一堆时先取,要想达成这个局面,则需要倒数第二堆是你的对手取完,如果让A来接受6,7,11的盘的话,那包是A赢,A会想办法让B接手1,x的局面。所以没有条件限制的情况下,A非常厉害,但是情况就是有限制条件,就是(1,2,3…)这种连续。但也只是影响顺序

#include <bits/stdc++.h>
#define int long long 
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define fi first
#define se second
#define PII pair <int,int>
#define ALL(x) x.begin(),x.end()
#define lowbit(x) (x&(-x))
using namespace std;
const int N = 1e6+5;
int a[N],b[N];

void solve () {
	int n;cin>>n;
	set<int>se;
	for (int i=1;i<=n;i++) {
		int x;cin>>x;
		se.insert(x);
	}
	int e=0,cnt=0;
	int l=se.size();
	for (auto t : se) {
		if (t-e==1) {
			cnt++;
			e=t;
		}
		else break;
	}//这里的遍历就是找1,2,3,4,5....
	if (cnt==l) {
		if (cnt&1) cout<<"Alice"<<'\n';
		else cout<<"Bob"<<'\n';
		return ;
	}
	if (cnt&1) cout<<"Bob"<<'\n';
	else cout<<"Alice"<<'\n';
}

signed main () {
	IOS;
	int T =1;
	cin>>T;
	while(T--) solve ();
	return 0;
}

MEX GAME 1

来源于CF
这一题耗费了我好久才写出来,非常符合博弈论的题,耗时但是代码量短。
题意见如下图
在这里插入图片描述
题意很好理解,给出一段序列,Alice和Bob轮流选,Alice想让序列尽可能大,Bob想让序列尽可能小,序列的大小取决于Alice选择的序列中没出现的最小非负整数 例如 2,2,1的大小是0。3,1,0,1的大小是2
问给出的序列大小最后是多少
Alice要想让序列最大,那么Alice选择的序列就要充满0,1,2,3,4这样的数。第一思路就是将出现的数字都统计一下用map。经过一些列思考后我们可以发现,Alice可以先选一个只出现一个的数字,后面Bob选择哪个我们就选择哪个,这样除非Bob删掉一个序列中只出现一次的数字,这样我们这个数字就选不了,但是这个Bob选择的数字之前的所有数字我们都可以选到,例如0,0,1,1,2,3,3,4,4,5,6,6;这个序列,我们先选2,如果Bob选0,那么我们也选0,Bob选1,我们也选1.但当Bob选5了,Alice的序列中就不可能出现5,所以我们序列的最大值就是5。
总结一下就是 这里我们Alice要先选择一个序列中第一个只出现一次的数字,后面出现的第二个单个数字就是我们的答案。但是0,0,1,2,2,4,4,6这个序列的答案就是3,因为3没有出现过。

void solve () {
	int n;cin>>n;
	map<int,int>mp;
	for (int i=1;i<=n;i++) {
		cin>>a[i];mp[a[i]]++;
	}//用map存储一下每个数字出现的次数
	int f=1;
	for (int i=0;i<=n;i++) {
		if (mp[i]==0) {
			cout<<i<<'\n';return ;
		}
		if (f==0&&mp[i]==1) {
			cout<<i<<'\n';return ;
		}
		if (f==1&&mp[i]==1) {
			f=0;
		}	
	}	
}

日历游戏

来源于牛客
题意也是符合博弈论的特点–容易读。
 题意就是在2000.1.1到2024.8.1之间给出日期,要求每次只能月份加一或者日份加一。问从给出的日期到2024.8.1谁先到,Alice先手,Alice有没有必胜的把握
 思考:要想到2024.8.1就要从7.31或者7.1该Alice走,按照这个规律往下推可以看出6.30该Alice走也是必胜的,Alice让6.30到7.30,Bob只能让7.30到7.31,这就直接必胜了。从6.30我们可以看到6.28也是必胜的往下推我们发现,只有当月份加日份为偶数的时候就是必胜的。但是有两个值需要特判,就是只有30天的月份时的30号,例如11.30,虽然相加是奇数,但是我Alice将11.30变为12.1,这样Alice下一步又是偶数可以操作了。

void solve () {
	int a,b,c;cin>>a>>b>>c;
	int f=0;
	if (b==9&&c==30) {
		f=1;
	}
	if (b==1&&c==30) f=1;
	if ((b+c)%2==0) f=1;
	if (f==1) cout<<"YES"<<'\n';
	else cout<<"NO"<<'\n';
}

直接特判两个然后判断奇偶性直接就出来了。看着很难,但实际一点也不简单

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值