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;
}