BZOJ2446. 石头剪刀布 数学+概率+分数规划(二分+dp check)

BZOJ2446. 石头剪刀布

题面:

描述:

\(A\)\(B\)进行石头剪刀布的游戏。

游戏按照每轮进行,每轮进行\(n\)局,如果\(A\)先比\(B\)多获胜\(m_1\)轮,\(A\)赢得游戏;如果\(B\)先比\(A\)多获胜\(m_2\)轮,\(B\)赢得游戏。

对于每轮,进行\(n\)局游戏,已知每局\(B\)出石头、剪刀和布的概率,每局如果\(A\)赢,则\(A\)得一分;如果\(B\)赢,则\(B\)得一分;如果平局,则无人得分。最后得分多的人此轮获胜,得分相同此轮无人获胜。

\(A\)获胜的最大概率。

输入:

读入包含多组数据。

对于每组数据:

第一行包含\(3\)个数,\(n,m_1,m_2\),如题面描述。

接下来\(n\)行,每行\(3\)个数,分别表示\(B\)每局出石头、布与剪刀的百分率,保证三个数之和为\(100\)

整个输入以0 0 0结束

输出:

对于每组数据输出一行,表示获胜的百分率,保留\(3\)位小数。

样例输入:

2 1 1
50 50 0
0 0 100
1 2 1
20 20 60
5 3 1
32 47 21
25 37 38
34 40 26
49 20 31
25 60 15
5 3 1
41 13 46
13 27 60
50 24 26
14 32 54
25 56 19
10 3 1
21 33 46
31 25 44
24 32 44
39 39 22
21 39 40
30 25 45
49 21 30
30 24 46
20 33 47
43 28 29
10 3 1
30 20 50
44 23 33
31 28 41
28 48 24
35 26 39
29 34 37
47 31 22
32 43 25
25 29 46
30 42 28
362 91 3
34 35 31
33 33 34
33 33 34
33 34 33
33 32 35
33 35 32
34 33 33
31 35 34
32 35 33
35 34 31
33 34 33
34 31 35
33 34 33
33 35 32
32 34 34
34 34 32
32 35 33
32 34 34
32 35 33
34 32 34
35 32 33
34 35 31
35 34 31
34 31 35
35 34 31
33 34 33
32 35 33
33 33 34
32 33 35
34 32 34
35 34 31
35 33 32
34 33 33
34 34 32
34 32 34
33 32 35
32 33 35
35 34 31
32 35 33
35 34 31
35 33 32
35 31 34
33 34 33
32 33 35
35 32 33
32 34 34
32 35 33
32 34 34
33 35 32
34 34 32
33 34 33
35 34 31
32 34 34
34 32 34
35 33 32
33 34 33
34 35 31
33 33 34
35 32 33
35 34 31
33 33 34
31 35 34
33 33 34
34 35 31
33 34 33
32 35 33
32 34 34
33 33 34
31 35 34
34 31 35
34 34 32
35 32 33
33 32 35
34 32 34
35 34 31
35 31 34
34 33 33
34 32 34
31 35 34
35 32 33
34 33 33
32 35 33
33 35 32
33 35 32
33 34 33
32 33 35
32 33 35
31 34 35
33 33 34
35 31 34
31 35 34
34 31 35
35 31 34
32 34 34
34 35 31
35 33 32
34 31 35
34 31 35
34 31 35
34 33 33
33 33 34
34 31 35
33 34 33
35 33 32
32 35 33
32 33 35
31 34 35
33 35 32
34 32 34
34 34 32
33 32 35
35 34 31
34 35 31
32 33 35
32 35 33
34 31 35
35 32 33
34 31 35
34 35 31
31 34 35
35 33 32
31 35 34
34 35 31
34 31 35
35 33 32
34 33 33
34 31 35
34 31 35
34 34 32
33 34 33
34 32 34
34 35 31
34 34 32
32 35 33
35 32 33
35 32 33
35 31 34
35 32 33
33 33 34
31 35 34
32 33 35
31 34 35
34 33 33
34 32 34
32 33 35
33 34 33
34 33 33
35 32 33
32 33 35
32 34 34
32 35 33
33 35 32
35 33 32
34 31 35
32 33 35
33 35 32
31 35 34
34 31 35
33 34 33
34 35 31
33 32 35
34 32 34
33 35 32
34 35 31
31 35 34
33 33 34
32 34 34
34 33 33
35 33 32
34 34 32
33 33 34
32 34 34
33 32 35
34 31 35
34 32 34
33 32 35
32 34 34
35 32 33
34 31 35
34 32 34
33 32 35
34 32 34
35 31 34
34 33 33
31 35 34
33 35 32
32 34 34
31 34 35
33 34 33
34 34 32
34 32 34
35 31 34
32 33 35
34 31 35
34 34 32
32 34 34
32 34 34
32 33 35
31 34 35
32 34 34
33 35 32
33 33 34
35 33 32
31 35 34
35 32 33
31 34 35
32 34 34
33 32 35
35 32 33
35 31 34
34 32 34
32 34 34
31 34 35
34 35 31
34 34 32
35 31 34
31 35 34
33 34 33
34 34 32
33 33 34
35 33 32
34 33 33
34 33 33
34 33 33
33 35 32
35 34 31
33 33 34
34 35 31
31 35 34
32 35 33
32 33 35
35 34 31
33 33 34
35 32 33
32 33 35
34 31 35
35 34 31
34 32 34
31 34 35
35 34 31
35 31 34
32 33 35
33 33 34
32 33 35
34 31 35
31 35 34
35 32 33
34 35 31
33 32 35
34 32 34
33 32 35
34 33 33
33 33 34
31 35 34
35 34 31
35 32 33
34 31 35
32 35 33
34 34 32
35 33 32
33 35 32
34 31 35
33 34 33
34 33 33
34 35 31
34 32 34
32 35 33
32 33 35
35 32 33
34 32 34
33 33 34
33 33 34
35 31 34
34 35 31
34 35 31
35 31 34
34 32 34
31 34 35
33 33 34
32 34 34
34 32 34
32 35 33
33 32 35
33 32 35
33 33 34
35 33 32
31 34 35
35 31 34
33 34 33
31 34 35
34 35 31
31 34 35
32 35 33
33 35 32
34 35 31
32 35 33
34 32 34
32 33 35
32 34 34
35 33 32
32 35 33
33 32 35
35 34 31
34 35 31
33 33 34
33 32 35
32 33 35
33 34 33
35 32 33
31 35 34
31 34 35
32 35 33
33 34 33
31 35 34
33 34 33
34 33 33
33 33 34
32 34 34
32 35 33
33 35 32
34 31 35
34 35 31
31 34 35
35 33 32
35 34 31
31 34 35
34 31 35
32 35 33
34 31 35
33 35 32
34 35 31
34 33 33
34 34 32
34 33 33
32 33 35
32 34 34
35 33 32
35 32 33
34 34 32
32 35 33
34 33 33
33 35 32
33 35 32
33 35 32
35 31 34
34 33 33
33 33 34
34 31 35
34 35 31
34 31 35
32 34 34
35 33 32
35 32 33
35 34 31
34 32 34
32 34 34
35 32 33
33 34 33
32 33 35
35 31 34
34 35 31
33 35 32
0 0 0

样例输出:

100.000%
69.231%
64.981%
80.864%
72.400%
69.287%
94.322%

范围约定:

对于\(40\%\)的测试点,\(n\leq 500\)

对于\(100\%\)的测试点,\(n\leq 1000,m_1,m_2\leq 100\)

分析:

游戏按轮进行。

对于每一轮,不妨设\(A\)赢的概率为\(p_1\)\(B\)赢的概率为\(p_0\),如何计算最终\(A\)获胜的概率呢?

不妨设\(f(x)\)为当\(A\)\(B\)多赢\(x\)轮时,\(A\)最终获胜的概率(为了方便,如果\(B\)\(A\)多赢\(y\)轮,我们认为\(A\)\(B\)多赢\(-y\)轮)

\(A\)\(B\)多赢\(x\)轮时,

\(p_1\)的概率进入\(A\)\(B\)多赢\(x+1\)轮的状态。

\(p_0\)的概率进入\(A\)\(B\)多赢\(x-1\)轮的状态。

\((1-p_0-p_1)\)的概率停留在\(A\)\(B\)多赢\(x\)轮的状态。

\(p_1=0\),则\(A\)获胜概率为\(0\)

\(p_1\neq0\)时,根据全概率公式及题意,得到如下递推式及初始状态

\[f(x)=p_1f(x+1)+p_0f(x-1)+(1-p_0-p_1)f(x)\\ f(m_1)=1\\ f(-m_2)=0 \]

这是一个常系数齐次差分方程,通过求解特征方程,算出通解后代入初始条件解任意常数,最终可以解得满足初始条件的特解为

\[f(x)=\frac{(\frac{p_0}{p_1})^{x+m_2}-1}{(\frac{p_0}{p_1})^{m_1+m_2}-1}\qquad(p_0\neq p_1)\\ f(x)=\frac{x+m_2}{m_1+m_2}\qquad(p_0=p_1) \]

求解过程:

将递推式化为

\[p_1f(x+1)-(p_0+p_1)f(x)+p_0f(x-1)=0 \]

其对应的特征方程为

\[p_1\lambda^2-(p_0+p_1)\lambda+p_0=0 \]

因式分解得

\[(p_1\lambda-p_0)(\lambda-1)=0 \]

解得

\[\lambda_1=\frac{p_0}{p_1}\\ \lambda_2=1 \]

\(p_0=p_1\),则通解为

\[f(x)=C_1+C_2x \]

由于\(f(m_1)=1,f(-m_2)=0\),故

\[\left\{\begin{aligned} C_1+C_2m_1=1\\ C_1-C_2m_2=0 \end{aligned}\right. \]

解得

\[C_1=\frac{m_2}{m_1+m_2}\\ C_2=\frac{1}{m_1+m_2}\\ \]

故此时

\[f(x)=\frac{x+m_2}{m_1+m_2} \]

\(p_0\neq p_1\),则通解为

\[f(x)=C_1\left(\frac{p_0}{p_1}\right)^x+C_2 \]

由于\(f(m_1)=1,f(-m_2)=0\),故

\[\left\{\begin{aligned} C_1\left(\frac{p_0}{p_1}\right)^{m_1}+C_2=1\\ C_1\left(\frac{p_0}{p_1}\right)^{-m_2}+C_2=0 \end{aligned}\right. \]

解得

\[C_1=\frac{(\frac{p_0}{p_1})^{m_2}}{(\frac{p_0}{p_1})^{m_1+m_2}-1}\\ C_2=-\frac{1}{(\frac{p_0}{p_1})^{m_1+m_2}-1} \]

故此时

\[f(x)=\frac{(\frac{p_0}{p_1})^{x+m_2}-1}{(\frac{p_0}{p_1})^{m_1+m_2}-1} \]

所以

\[f(0)=\frac{(\frac{p_0}{p_1})^{m_2}-1}{(\frac{p_0}{p_1})^{m_1+m_2}-1}\qquad(p_0\neq p_1)\\ f(0)=\frac{m_2}{m_1+m_2}\qquad(p_0=p_1) \]

所以最终获胜的概率是关于\(p_0/p_1\)的一个函数,不妨设这个函数为\(g(x)\)

\[g(x)=\left\{\begin{aligned} &\frac{x^{m_2}-1}{x^{m_1+m_2}-1},&&0<x<1\lor x>1\\ &\frac{m_2}{m_1+m_2},&&x=1 \end{aligned}\right. \]

通过求导(高中数学常见的导数题证明),可以发现这个函数单调递减,所以要使得\(g(x)\)最大,就需要让\(x\)尽量小。

简略证明:

\[\lim\limits_{x\rightarrow1}g(x)=\lim\limits_{x\rightarrow1}\frac{m_2\ln x}{(m_1+m_2)\ln x}=\frac{m_2}{m_1+m_2}=g(1) \]

所以\(g(x)\)\(x=1\)处连续。

\(a=m_2,b=m_1+m_2\),则有\(1\leq a<b\)

\[g(x)=\frac{x^a-1}{x^b-1} \]

求导得

\[g'(x)=\frac{x^{a-1}[(a-b)x^b+bx^{b-a}-a]}{(x^b-1)^2} \]

\(h(x)=(a-b)x^b+bx^{b-a}-a\),则有

\[h'(x)=b(a-b)x^{b-1}+b(b-a)x^{b-a-1}\\ =-bx^{b-a-1}(b-a)(x^a-1) \]

可以得出\(h(x)\)\((0,1)\)上递增,\((1,+\infty)\)上递减,

\(h(1)=0\),所以\(h(x)\leq 0\),从而\(g'(x)\leq 0\),所以\(g(x)\)\((0,1)\)\((1,+\infty)\)上分别递减。

又有\(g(x)\)\(x=1\)处连续,所以有\(g(x)\)\((0,+\infty)\)上递减。

所以我们要求出\(p_0/p_1\)的最小值。

如果给定这\(n\)局的出拳策略,则可以很容易地在\(O(n^2)\)内使用动态规划的算法算出\(p_0\)\(p_1\),具体做法就是设\(f(i,j)\)为进行\(i\)局结束后,\(A\)获胜数减\(B\)获胜数为\(j\)的概率,容易写出状态转移方程。

问题是现在要使得\(p_0/p_1\)最小,我们不可能直接枚举策略。

想到设\(f(i,j)\)为已经进行到第\(i\)局结束,此时\(A\)获胜数减\(B\)获胜数为\(j\)这个状态的最小的\(p_0/p_1\),我们发现难以下手写转移方程。

注意到目标函数是一个分式,我们不妨考虑进行分数规划,整体的想法就是二分+可行性判断。

由于最小值可能非常大不便于二分,所以我们尝试令最小值\(ans=s/(1-s)(0\leq s<1)\)(构造一个双射将\([0,+\infty)\)映射到\([0,1)\))。

这样只需要二分\(s\)即可,认为\(s\)为此时\(s\)的二分值。

如果存在一种出拳策略,使得\(p_0/p_1<s/(1-s)\),则\(s/(1-s)>ans\),反之\(s/(1-s)\leq ans\)

其等价于,若\((sp_1+(s-1)p_0)_{\max}>0\),则\(s>ans/(1+ans)\)(即\(s\)偏大,需要缩小),反之\(s\leq ans/(1+ans)\)(即\(s\)偏小,需要放大)。

现在问题就转化成了求\(sp_1+(s-1)p_0\)的最大值,显然,这是需要用动态规划解决的。

\(f(i,j)\)为,已知进行到第\(i\)局结束时,\(A\)获胜数减\(B\)获胜数为\(j\),最大的\(sp_1+(s-1)p_0\),由于\(sp_1+(s-1)p_0\)是关于概率\(p_1\)\(p_0\)的线性组合,所以根据概率乘法公式状态转移比较容易写出。

\[f(i,j)=\max_{k\in\{0,1,2\}}\{f(i+1,j-1)p(i,k)+f(i+1,j)p(i,(k+2)\%3)+f(i+1,j+1)p(i,(k+1)\%3)\} \]

(注:\(k=0,1,2\)分别表示\(A\)这局的策略为剪刀,石头,布,\(p(i,0),p(i,1),p(i,2)\)分别表示\(B\)\(i+1\)局出石头,布,剪刀的概率)

初始状态

\[f(n,i)=\begin{cases} s-1,&i<0\\ 0,&i=0\\ s,&i>0\\ \end{cases} \]

代码:

#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
int n, m1, m2;
const int maxn = 1010;
const double eps = 1e-8;
int p[maxn][3];
double f[maxn][maxn];
bool check(double s) {
    for (int i = 0; i < n; i++) {
        f[n][i] = s - 1;
    }
    f[n][n] = 0;
    for (int i = n + 1; i <= 2 * n; i++) {
        f[n][i] = s;
    }
    for (int i = n - 1; i >= 0; i--) {
        for (int j = n - i; j <= n + i; j++) {
            f[i][j] = (f[i + 1][j - 1] * p[i][0] + f[i + 1][j] * p[i][2] +
                       f[i + 1][j + 1] * p[i][1]) /
                      100;
            for (int k = 1; k < 3; k++) {
                f[i][j] = max(f[i][j], (f[i + 1][j - 1] * p[i][k] +
                                        f[i + 1][j] * p[i][(k + 2) % 3] +
                                        f[i + 1][j + 1] * p[i][(k + 1) % 3]) /
                                           100);
            }
        }
    }
    return f[0][n] > eps;
}
void solve() {
    double l = 0, r = 1;
    bool flag;
    while (r - l > eps) {
        double mid = (l + r) / 2;
        flag = check(mid);
        if (flag) {
            r = mid;
        } else {
            l = mid;
        }
    }
    if (l - 0.5 < eps && l - 0.5 > -eps) {
        printf("%.3lf%%\n", (double)m2 / (m1 + m2) * 100);
    } else {
        double ans = l / (1 - l);
        printf("%.3lf%%\n", (pow(ans, m2) - 1) / (pow(ans, m1 + m2) - 1) * 100);
    }
}
int main() {
    while (scanf("%d%d%d", &n, &m1, &m2) == 3 &&
           !(n == 0 && m1 == 0 && m2 == 0)) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < 3; j++) {
                scanf("%d", &p[i][j]);
            }
        }
        solve();
    }
    return 0;
}
posted @ 2021-09-08 02:12  聆竹听风  阅读(248)  评论(0)    收藏  举报