CF98E Help Shrek and Donkey

一、题目

点此看题

二、解法

首先我们要弄清楚先后手玩游戏大致用什么策略,由于每张牌都是等价的,所以乱猜还不如去调戏一下对手,那么感性理解在不确定桌上卡牌的情况下,先后手是不会使用猜测操作的,证明

因为如果询问操作后对方没有弃牌,那么后手可以认为牌是在牌堆,从而做出猜测操作。这题有趣的地方是可以询问自己的手牌,可以诱导后手输掉,但是后手也可以选择相信或者不相信。

\(f(n,m)\) 为先手选 \(n\) 张牌,后手选 \(m\) 张牌的概率,我们可以列一个策略的表格:

先后手 ............................询问............................ ............................欺骗............................
询问 \(\frac{m}{m+1}(1-f(m-1,n))\) \(\frac{1}{m+1}+\frac{m}{m+1}(1-f(m-1,n))\)
欺骗 \(1\) \(1-f(m,n-1)\)

就拿先手询问,后手认为先手是欺骗的情况来说吧,其他情况可以类似推导。此时有 \(\frac{1}{m+1}\) 的概率抽中桌上的卡牌,但是后手以为先手在欺骗无动于衷,所以先手可以在以后的操作中直接猜测,必胜。有 \(\frac{m}{m+1}\) 的概率抽中对方的手牌,那么此时先后手交换,原来的后手可以看成失去了一张牌,所以原来后手的胜率是 \(f(m-1,n)\),原来先手的胜率是 \(1-f(m-1,n)\)

设先手有 \(p\) 的概率询问,\(1-p\) 的概率欺骗,设表格中的概率按从左到右的顺序分别是 \(a,b,c,d\),那么:

\[pa+(1-p)c=pb+(1-p)d \]

\[p=\frac{d-c}{a-b-c+d} \]

那么可以根据 \(p\) 来转移了,但是为什么要这样做呢?下面给出解释(感谢 \(\tt Charlotte\) 大佬的讲解):

“主要是两人其实除了自己和对方有几张牌以外就没有其他信息了”

“所以操作基本只能基于当前局面”

“而基于当前局面,可以选择欺骗或猜测两种方案”

“那这两种方案并没有哪一个是必定占优势的”

“就只能平衡两者的收益,按某种概率选择其中一种策略,来达到期望最优”

上面这段话解释了为什么先手要按照概率 \(p\) 来决策,那么解出 \(p\) 的等式是怎么来的呢?后手选择 认为先手询问 和 认为先手欺骗 的收益应该是相同的,否则后手会倾向收益高的那边,那么先手就会调整 \(p\) 使得两者相同。

三、总结

信息不对等博弈可以考虑按概率决策,并且寻找收益平衡点。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 1005;
#define db double
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m;db g[M][M];
db f(int n,int m)
{
	if(g[n][m]) return g[n][m];
	db a=m*(1-f(m-1,n))/(m+1.0),b=1/(m+1.0)+a;
	db c=1,d=1-f(m,n-1),p=(d-c)/(a-c-b+d);
	return g[n][m]=p*a+(1-p)*c;
}
signed main()
{
	n=read();m=read();
	for(int i=0;i<=max(n,m);i++)
		g[i][0]=1,g[0][i]=1.0/(i+1);
	printf("%.10f %.10f\n",f(n,m),1-f(n,m));
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15789196.html