[CF98E] Help Shrek and Donkey
\(\text{Problem}:\)Help Shrek and Donkey
\(\text{Solution}:\)
设 \(f_{n,m}\) 表示先手剩 \(n\) 张牌,后手剩 \(m\) 张牌,先手的胜率。
首先显然的,当可以获得更多的信息时,一定不会选择直接猜测。故先手有两种决策:
-
指定一张他手上没有的牌(记为 \(1\) 操作)。
-
指定他手上的牌来欺骗后手(记为 \(2\) 操作)。
与此对应的,后手也有两种决策:认为先手没有/有欺骗自己(分别记为 \(1/2\) 操作)。
那么现在考虑以下四种情况的转移:
- 操作 \(11\)。先手指定了一张他手上没有的牌。如果这张牌不在后手手上那么先手必败;否则,后手丢掉这张牌并转化为先手,从 \(\frac{m}{m+1}(1-f_{m-1,n})\) 转移而来。
- 操作 \(12\)。先手指定了一张他手上没有的牌。如果这张牌在后手手上,则从 \(\frac{m}{m+1}(1-f_{m-1,n})\) 转移而来;否则,后手认为这张牌必然不是桌上的牌,那么先手必胜,从 \(\frac{1}{m+1}\) 转移而来。
- 操作 \(21\)。先手指定了他手上的牌。后手
愚蠢地认为这张牌在桌上,于是猜错,那么先手必胜,从 \(1\) 转移而来。 - 操作 \(22\)。先手指定了他手上的牌。后手认为先手在骗自己,那么等同于先手丢掉了这张牌,后手转化为先手,从 \(1-f_{m,n-1}\) 转移而来。
同时还有边界情况。当 \(m=0\) 时,先手一定知道桌上的牌,故胜率为 \(1\);当 \(n=0\) 时,先手不猜就必败,故胜率为 \(\frac{1}{m+1}\)。
考虑这个游戏先手的收益取决于后手的决策,即后手希望最小化先手获胜的概率。所以这是一个混合策略纳什均衡,而现在问题转化为求出不动点。
设先手做操作 \(1\) 的概率为 \(p\),则做操作 \(2\) 的概率为 \(1-p\)。在纳什均衡中,由于先手希望无论后手采用怎样的策略都不会改变自己的期望收益,故列出方程:
\[value=p\cdot w_{1,1}+(1-p)\cdot w_{2,1}=p\cdot w_{1,2}+(1-p)\cdot w_{2,2}
\]
解出 \(p\) 即可求出 \(value\) 的值。记忆化搜索即可,时间复杂度 \(O(nm)\)。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=1010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
double f[N][N];
double F(int n,int m)
{
if(f[n][m]) return f[n][m];
if(!m) return f[n][m]=1.0;
if(!n) return f[n][m]=1.0/(m+1);
double w11=1.0*m/(m+1)*(1-F(m-1,n));
double w21=1.0;
double w12=w11+1.0/(m+1);
double w22=1.0-F(m,n-1);
double p=(w21-w22)/(w12+w21-w11-w22);
return f[n][m]=p*w11+(1.0-p)*w21;
}
signed main()
{
int m,n;
m=read(), n=read();
printf("%.10lf %.10lf\n",F(m,n),1.0-F(m,n));
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。