CF98E Help Shrek and Donkey

发布时间 2023-07-20 18:24:45作者: Ender_32k

第一次做这种非合作博弈均衡的题。

显然,当双方均有牌的情况下,先手是不可能直接指定桌牌的:正确的概率为 \(\frac{1}{m+1}\),错误的概率为 \(\frac{m}{m+1}\),显然 \(\frac{m}{m+1}\ge\frac{1}{m+1}\)

于是先手指定桌牌的情况只能是 \(n=0\)\(m=0\)

  • \(n=0\) 时,猜测没用(否则后手必胜),直接指定,正确的概率为 \(\frac{1}{m+1}\)
  • \(m=0\) 时,先手直接指定自己没有的那张牌即可,概率为 \(1\)

下面讨论 \(n,m\neq 0\) 的情况。

考虑到先手进行猜测有两种情况:猜测自己有的牌、猜测自己没有的牌。称猜测自己有的牌为“诈骗”,猜测没有的为“询问”,分类讨论:

先手猜测的牌设为 \(x\)

  • 若先手诈骗:

后手可以选择认为先手是询问,此时后手没有 \(x\) 并且认为先手没有 \(x\),所以他认为桌牌是 \(x\),于是这种情况下先手诈骗成功的概率为 \(a=1\)

后手可以选择认为先手是诈骗,此时后手知道先手有 \(x\),等价于先手摊牌 \(x\),下一轮先后手交换,即 \(b=1-f(m,n-1)\)

  • 若先手询问:

那么先手有 \(\frac{m}{m+1}\) 概率猜中对方的牌,有 \(\frac{1}{m+1}\) 概率猜到桌牌。

无论后手选择先手是诈骗还是询问,如果猜中了对方的牌,对方都要摊牌,也就是两边的概率都有 \(\frac{m}{m+1}\left(1-f(m-1,n)\right)\)

如果没猜中对方的牌(猜中了桌牌)。如果后手认为先手在询问,那么下一轮他就会直接指定桌牌,此时先手获胜概率为 \(0\);如果后手认为先手在诈骗,后手下一轮就不会指定桌牌,而先手已经知道自己没有的 \(x\) 后手也没有,会直接指定,此时先手概率为 \(\frac{1}{m+1}\)

所以如果此时后手认为先手在询问,先手获胜概率为 \(c=\frac{m}{m+1}(1-f(m-1,n))\);否则后手认为先手在诈骗,先手获胜概率为 \(d=\frac{m}{m+1}(1-f(m-1,n))+\frac{1}{m+1}\)


综合一下,发现先手只要选择了是诈骗还是询问,获胜概率全由后手选择认为先手是诈骗还是询问决定,设先手选择询问的概率为 \(p\)

  • 后手认为先手询问,获胜概率 \(p_1=(1-p)a+pc=(1-p)+\frac{mp}{m+1}(1-f(m-1,n))\)
  • 后手认为先手诈骗,获胜概率 \(p_2=(1-p)b+pd=(1-f(m,n-1))(1-p)+\frac{mp}{m+1}(1-f(m-1,n))+\frac{p}{m+1}\)

根据纳什均衡策略\(p_1=p_2\),解得 \(p=\dfrac{(m+1)f(m,n-1)}{(m+1)f(m,n-1)+1}\),于是我们递归求得 \(p\) 再带入 \(p1,p2\) 中任意一个就可以求出 \(f(n,m)\) 了。

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

namespace vbzIO {
	char ibuf[(1 << 20) + 1], *iS, *iT;
//	#if ONLINE_JUDGE
//	#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
//	#else
	#define gh() getchar()
//	#endif
	#define rd read
	#define wr write
	#define pc putchar
	#define pi pair<int, int>
	#define mp make_pair
	#define fi first
	#define se second
	#define pb push_back
	#define ins insert
	#define era erase
	inline int read () {
		char ch = gh();
		int x = 0;
		bool t = 0;
		while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
		while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
		return t ? ~(x - 1) : x;
	}
	inline void write(int x) {
		if (x < 0) {
			x = ~(x - 1);
			putchar('-');
		}
		if (x > 9)
			write(x / 10);
		putchar(x % 10 + '0');
	}
}
using vbzIO::read;
using vbzIO::write;

const int N = 1e3 + 100;
int n, m;
double f[N][N];

double dp(int n, int m) {
	if (f[n][m] != 0) return f[n][m];
	if (n == 0) return 1. / (m + 1);
	if (m == 0) return 1;
	double p = (m + 1) * dp(m, n - 1) / (1 + (m + 1) * dp(m, n - 1));
	return f[n][m] = (1 - p) + 1. * m / (m + 1) * (1 - dp(m - 1, n)) * p;
}

int main() {
	n = rd(), m = rd();
	printf("%.9lf %.9lf", dp(n, m), 1 - dp(n, m));
	return 0;
}