HNU2019暑期训练赛之7 F. Left-Right-Win

Problem Description

The game of Left-Right-Win is played by n players around a table with a spinner* which returns Left
with probability P_left, Right with probability P_right and Win with probability 1 – P_left – P_right.

The game begins with the starting player (labeled 0 in the figures with other players labeled 1 … (n-1) clockwise) who spins the spinner. If the result is Win, he wins the pot of say $100. If the result is Left, the spinner is passed to the player on his/her left (player 1). If the result is Right, the spinner is passed to the player on his/her right (player (n-1)). The new holder of the spinner repeats this process: winning or passing the spinner to the left or right. The game continues until someone wins. (Since the game could, theoretically, go on forever, there is a stopping rule such as: after 2n turns, the house takes the pot.)

The figure on left shows the probabilities of each of 6 players winning with P_left = 0.5 and P_right =
0.4. The figure on the right shows the probabilities of each of 6 players winning with P_left = 0.1 and
P_right = 0.8.

Clearly, player 3 is not going to want to pay as much as player 0 to play the game. We would like each player to contribute to the pot an amount proportional to the probabilyt of their winning it (the potentially infinite game). Write a program which computes the amount each player should contribute to a $100 pot given P_left and P_right.

Computations should be done in double precision floating point.

A spinner is a device which returns one of several values with specified probabilities for each value. Originally, a spinner was a balanced pointer on a card with arcs around the center labeled with the different outcomes. You spin the pointer and where it lands is the chosen value. The arc lengths are proportional to the probabilities of the values. A spinner may be simulated by any random device. For instance, the P_left = 1/3 and P_right = 1/3 may be simulated by rolling a single die and specifying Left for 1 or 2, Right for 3 or 4 and Win for 5 or 6.
Input
The first line of input contains a single decimal integer P, (1 <= P <= 100), which is the number of data sets that follow. Each data set should be processed identically and independently.

Each data set consists of a single line of input containing 5 space separated values: the data set number, K, followed by the number of players, n, a decimal integer: (3 <= n <= 15), followed by the label of the player k, a decimal integer, (0<=k<=n-1), whose contribution is to be found, followed by two floating point values P_left and P_right. (P_left+P_right ≥ 0.8).

Assume a pot of $100 for all datasets.

Output
For each data set there is one line of output. The single output line consists of the data set number, K, followed by a single space followed by the contribution of player k to the $100 pot in dollars to two decimal places (i.e. dollars and cents).

Sample Input

3
1 6 0 .5 .4
2 6 3 .1 .8
3 6 2 .9 .03

Sample Output

1 25.75
2 15.01
3 17.13

Solution

 第一次做的时候,只是找到了规律,今日再读此题,霍然开朗,才算是彻底搞懂(当时可能是到了第4个小时疲惫了)
原理很简单,关键是区分好到达概率和Win的概率,列出方程高斯消元即可

在网上还找到了个O(n)的做法,很厉害
https://blog.csdn.net/baiyifeifei/article/details/88677754

AC Code (O(n))

#include<bits/stdc++.h>

using namespace std;
double lp, rp;
double c[20], d[20];
int n, k;

double getAnswer() {
    c[0] = 1, c[1] = 0;
    d[0] = 0, d[1] = 1;
    double sumC = 1.0, sumD = 1.0;
    for (int i = 2; i < n; i++) {
        c[i] = (1 / rp) * c[i - 1] - (lp / rp) * c[i - 2];
        d[i] = (1 / rp) * d[i - 1] - (lp / rp) * d[i - 2];
        sumC += c[i], sumD += d[i];
    }
    double a = 1 - lp * c[n - 1], b = -lp * d[n - 1] - rp, e = sumC, f = sumD, s1 = 1 - rp - lp, s2 = 1;
    double dp0 = (s1 * f - s2 * b) / (a * f - e * b), dp1 = (s1 * e - s2 * a) / (b * e - f * a);
    return c[k] * dp0 + d[k] * dp1;
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int caseNum;
        scanf("%d%d%d%lf%lf", &caseNum, &n, &k, &lp, &rp);
        printf("%d %.2f\n", caseNum, 100 * getAnswer());
    }
    return 0;
}

AC Code (O(n^2))

#include <iostream>
#include <cmath>
#include <iomanip>

using namespace std;
const int maxn = 55;
typedef double Matrix[maxn][maxn];
Matrix a;

void gauss_elimination(Matrix A, int n) {
    int i, j, k, r;
    for (i = 0; i < n; i++) {
        r = i;
        for (j = i + 1; j < n; j++)
            if (fabs(A[j][i]) > fabs(A[r][i]))
                r = j;
        if (r != i)
            for (j = 0; j <= n; j++)
                swap(A[r][j], A[i][j]);
        for (k = i + 1; k < n; k++) {
            double f = A[k][i] / A[i][i];
            for (j = i; j <= n; j++)
                A[k][j] -= f * A[i][j];
        }
    }
    for (i = n - 1; i >= 0; i--) {
        for (j = i + 1; j < n; j++)
            A[i][n] -= A[j][n] * A[i][j];
        A[i][n] /= A[i][i];
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t, n, p;
    double pl, pr;
    cin >> t;
    while (t--) {
        int caseNum;
        cin >> caseNum >> n >> p >> pl >> pr;
        for (int i = 0; i < n; i++) {
            a[i][i] = -1.0;
            a[i][(i + 1) % n] = pr;
            a[i][(i + n - 1) % n] = pl;
        }
        a[0][n] = pr + pl - 1;
        gauss_elimination(a, n);
        cout << caseNum << " ";
        cout << fixed << setprecision(2) << a[p][n] * 100.0 << "\n";
        for (int i = 0; i <= n; i++)
            for (int j = 0; j <= n; j++)
                a[i][j] = 0;
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值