CF98E Help Shrek and Donkey
第一次做这种非合作博弈均衡的题。
显然,当双方均有牌的情况下,先手是不可能直接指定桌牌的:正确的概率为 \(\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;
}