CF98E Help Shrek and Donkey(博弈论)
题目大意
有一副互不相同的牌共 (n+m+1) 张。有两个人,第一个人 (n) 张,第二个人 (m) 张。另外有一张牌放在桌子上。
两个人玩游戏轮流操作(其中第一个人为先手)。有如下 (2) 中操作类型:
-
猜测:猜桌上的那张牌是什么。如果猜对了则获胜,猜错了则失败。操作完之后游戏结束。
-
指定:报一张牌的名字,如果对方手上有这张牌,则将该牌丢弃,游戏继续;如果对方手上没有这张牌,对方则会表示他不用由此牌。
现在假设两个人都知道这 (n+m+1) 张牌分别是什么,但是不知道桌上和对方手里的牌具体是什么。
若双方都采取最优策略进行游戏,问先手和后手获胜概率。
若选手答案与标准答案的误差不超过 (10^{-9}) 则认为正确。
数据范围
数据范围:(0le n,mle 1000)
解题思路
博弈论神题
如果你认为这只是一道简单的概率 dp,那么 E 题的称号将臭名昭著
这题难在你猜对方,对方发现你没猜中,那么那张牌一定是桌子上的吗?显然不是,你可以故意猜自己手中的牌,诱使对方猜错
啊这,博弈论还可以欺骗的话还怎么玩啊(主要是不好做题)
设 (f[n][m]) 表示先手赢的概率,(p) 表示先手猜测的概率,列出一个表格
相信 | 不信 | |
---|---|---|
欺骗 | (a = 1) | (b = (1-f[m][n-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*c+(1-p)*a=p*d+(1-p)*b) 即可
为什么是这样,难道后手可以猜到先手的欺骗的概率,或先手猜到后手的相信的概率吗
考虑如果后手一直选择相信或不信,那么可以看到先手的收益是两条斜率正负不同的直线,但后手总可以根据先手两条直线的斜率大小确定自己最适合的“相信概率”,而这个概率能让先手在交点处以外的点都没有在交点处更优,所以先手选交点即可
代码
const int N = 2005;
int m, n;
double f[N][N];
int main() {
read(n), read(m); int mx = max(n, m);
for (int i = 0;i <= mx; i++) f[i][0] = 1;
for (int i = 1;i <= mx; i++) f[0][i] = 1.0 / (i + 1);
for (int i = 2;i <= n + m; i++) {
for (int j = 1;j < i; j++) {
if (i - j < 1) continue;
int x = j, y = i - j;
double inv = 1.0 / (y + 1);
double c = inv * y * (1 - f[y-1][x]), b = (1 - f[y][x-1]);
double d = c + inv;
double p = (1.0 - b) / (1 - c + d - b);
f[x][y] = p * c + (1 - p);
}
}
printf ("%.9lf %.9lf
", f[n][m], 1 - f[n][m]);
return 0;
}