题目链接:https://vjudge.net/problem/HDU-4870
将问题转换为每次可能+1分,-2分,目标到达20分。
且最终状态一定是一个账号19分,另一个账号20分。
因为期望的线性性,所以可以对两个账号分别计算期望,然后求和。
dp[u]表示以u分开始,到达20分的期望比赛场数,那么有dp[u] = p*(dp[u+1]+1) + (1-p)*(dp[u-2]+1)
那么最终答案就是dp[0]+dp[0]-dp[19]。
这题卡了精度,而且方程一定有解,所以高斯消元就不判断无解情况了。
(ps:对dp方程进行一下变形后,这题可以O(n)线性递推解决)
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define pb push_back
using namespace std;
const int N = 450;
const double eps = 1e-7;
double a[N][N],ans[N];
bool gauss(int n) {
rep(i, 0, n-1) { //逐列消除
int p = i;
rep(j, i+1, n-1) //找到该列最大系数所在的行p,减少误差(也可以找第一个非0系数,但误差会大一些)
if(fabs(a[p][i])<fabs(a[j][i]))
p = j;
swap(a[i],a[p]); //否则把p交换上来,用其进行消元
double div = a[i][i];
rep(j, i, n) a[i][j] /= div; //把p的第i列系数置1
rep(j, 0, n-1) { //开始消其他行
if(i==j) continue;
div = a[j][i];
rep(k, i, n)
a[j][k] -= a[i][k]*div;
}
}
rep(i, 0, n-1) ans[i] = a[i][n];
return 1;
}
int main() {
// freopen("a.txt","r",stdin);
double p;
while(cin>>p) {
memset(a,0,sizeof(a));
rep(i, 0, 19) {
a[i][i] += 1;
a[i][i+1] += -p;
a[i][max(i-2,0)] += -(1-p);
a[i][21] += 1;
}
a[20][20] += 1;
a[20][21] += 0;
gauss(21);
printf("%.8f\n",ans[0]+ans[0]-ans[19]);
}
return 0;
}