CF98E Help Shrek and Donkey
Solution
bunnny 之前搬的题,但是没有做🥵🥵🥵
我们不妨设 \(f(n,m)\) 表示先手有 \(n\) 张对手未知牌,后手有 \(m\) 张对手未知牌时先手获胜的概率。我们不妨先来观察一下双方的操作,先手可以查询自己没有的牌,也可以查询自己有的牌来达到欺骗的效果,后手就会有相信先手没有此牌或是觉得先手在欺骗自己的两种选择,不妨理解为“相信”和“不相信”。
我们首先考虑先手如果不欺骗,那么有 \(\dfrac{m}{m+1}\) 的概率询问到后手手上的牌,\(\dfrac{1}{m+1}\) 的概率询问到桌上的牌。如果询问到后手手上的牌,那么后手会拿出来他的牌,那么先手获胜的概率即为:\(\dfrac{m}{m+1}(1-f(m-1,n))\),如果询问到桌上的牌,那么后手如果相信了,则先手必输,否则先手必胜,先手获胜概率为 \(\dfrac{1}{m+1}\)。再考虑先手进行欺骗,查询自己的牌,如果后手相信了,那么显然先手就必胜了,否则就相当于知道了先手的一张牌,先手获胜概率即为:\(1-f(m,n-1)\)。需要注意的是,当先手进行询问时,当询问到后手所拥有的牌,后手也可以选择不相信,但会把这张牌放出去,从而先手产生 \(\dfrac{m}{m+1}(1-f(m-1,n))\) 的收益。这里感觉比较抽象,但是可以理解为,无论怎么样,只要先手询问到了后手手上的牌就会产生这样的贡献,与后手是否相信无关。
把上述情况列为表格,我们可以得到:
后手\先手 | 询问 | 欺骗 |
---|---|---|
相信 | \(\dfrac{m}{m+1}(1-f(m-1,n))\) | \(1\) |
不相信 | \(\dfrac{1}{m+1}+\dfrac{m}{m+1}(1-f(m-1,n))\) | \(1-f(m,n-1)\) |
为了方便,把上列的概率分别成为 \(w_{1,1},w_{1,2},w_{2,1},w_{2,2}\)。
我们考虑先手假设以 \(p\) 的概率询问,以 \(1-p\) 的概率进行欺骗,可以发现先手获胜的概率就是:
先手就需要最大化该值。可以看出,这个可以表示为两个斜率正负性相反的直线求 \(\min\) ,那么在交点处最为优秀。我们也可以从纳什均衡的角度出发,对于先手来说,无论后手如何选择收益都相同。
然后随便算一下就好了,复杂度 \(\mathcal O(n^2)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 1005
//char buf[1<<21],*p1=buf,*p2=buf;
//#define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,m;
double f[MAXN][MAXN];
double dp (int x,int y){
if (f[x][y]) return f[x][y];
if (!y) return f[x][y] = 1;
if (!x) return f[x][y] = 1.0 / (y + 1);
double w11 = y * 1.0 / (y + 1) * (1 - dp (y - 1,x)),w21 = 1,
w12 = w11 + 1.0 / (y + 1),w22 = 1 - dp (y,x - 1);
double p = (w21 - w22) / (w12 + w21 - w11 - w22);
return f[x][y] = p * w11 + (1 - p) * w21;
}
signed main(){
read (n,m);double p = dp (n,m);
printf ("%.9f %.9f\n",p,1 - p);
return 0;
}