题目链接
题意
求从1到100的期望次数,每次投一个1-6的色子走其显示步数,如果目标点超过100,这次操作不执行。
1到100之间存在单向的隧道可瞬移,需要恰好踩上去才可以
思路
由于存在环路,无法线性递推,需要构造一个方程组求解
对于存在可以瞬移的点a到b
d
p
[
a
]
−
d
p
[
b
]
=
0
dp[a] - dp[b] = 0
dp[a]−dp[b]=0
对于不会空过和传送的点a
d
p
[
a
]
=
d
p
[
a
+
1
]
+
d
p
[
a
+
2
]
+
d
p
[
a
+
3
]
+
d
p
[
a
+
4
]
+
d
p
[
a
+
5
]
+
d
p
[
a
+
6
]
6
+
1
dp[a] = \frac{dp[a+1] + dp[a+2] + dp[a+3] + dp[a+4] + dp[a+5] + dp[a+6]}{6}+1
dp[a]=6dp[a+1]+dp[a+2]+dp[a+3]+dp[a+4]+dp[a+5]+dp[a+6]+1
即
6
∗
d
p
[
a
]
−
d
p
[
a
+
1
]
−
d
p
[
a
+
2
]
−
d
p
[
a
+
3
]
−
d
p
[
a
+
4
]
−
d
p
[
a
+
5
]
−
d
p
[
a
+
6
]
=
6
6*dp[a] - dp[a+1] - dp[a+2] - dp[a+3] - dp[a+4] - dp[a+5] - dp[a+6] = 6
6∗dp[a]−dp[a+1]−dp[a+2]−dp[a+3]−dp[a+4]−dp[a+5]−dp[a+6]=6
对于目标点可能超过100的点,如96点的期望。
d
p
[
96
]
=
d
p
[
97
]
+
d
p
[
98
]
+
d
p
[
99
]
+
d
p
[
100
]
+
d
p
[
96
]
+
d
p
[
96
]
6
+
1
dp[96] = \frac{dp[97]+dp[98]+dp[99]+dp[100]+dp[96]+dp[96]} {6}+1
dp[96]=6dp[97]+dp[98]+dp[99]+dp[100]+dp[96]+dp[96]+1
4
∗
d
p
[
96
]
−
d
p
[
97
]
−
d
p
[
98
]
−
d
p
[
99
]
−
d
p
[
100
]
=
6
4*dp[96]-dp[97]-dp[98]-dp[99]-dp[100] = 6
4∗dp[96]−dp[97]−dp[98]−dp[99]−dp[100]=6
还有终点
d
p
[
100
]
=
0
dp[100] = 0
dp[100]=0
构造完矩阵高斯消元一波求出dp[1]即为期望。
代码
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define eps 1e-9
const int maxn = 105;
double mat[maxn][maxn], ans[maxn];
int equ = 100, var = 100;
double abb(double fuck)
{
return fuck < 0 ? -fuck : fuck;
}
int Gauss()
{
int i, j, k, col, max_r;
for(k = 0, col = 0; k < equ && col < var; k++, col++)
{
max_r = k;
for(i = k+1; i < equ; ++i) if(abb(mat[i][col]) > abb(mat[max_r][col])) max_r = i;
if(abb(mat[max_r][col] < eps)) return 0;
if(k != max_r)
{
for(j = col; j < var; ++j) swap(mat[k][j], mat[max_r][j]);
swap(ans[k],ans[max_r]);
}
ans[k] /= mat[k][col];
for(j = col+1; j < var; ++j) mat[k][j] /= mat[k][col];
mat[k][col] = 1;
for(i = 0; i < equ; ++i)
{
if(i != k)
{
ans[i] -= ans[k]*mat[i][col];
for(j = col+1; j < var; ++j) mat[i][j] -= mat[k][j] * mat[i][col];
mat[i][col] = 0;
}
}
}
return 1;
}
int vis[105];
int main()
{
int t, ca = 1;
for(scanf("%d",&t); ca <= t; ++ca)
{
memset(mat,0,sizeof(mat));
memset(ans,0,sizeof(ans));
memset(vis,0,sizeof(vis));
int m, a, b;
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&a,&b);
vis[a-1] = 1;
mat[a-1][a-1] = 1.0;
mat[a-1][b-1] = -1.0;
ans[a-1] = 0;
}
for(int i = 0; i < 94; ++i)
{
if(vis[i]) continue;
mat[i][i] = 6;
for(int j = 1; j < 7; ++j) mat[i][i+j] = -1;
ans[i] = 6;
}
for(int i = 94; i < 99; ++i)
{
if(vis[i]) continue;
mat[i][i] = 99-i;
for(int j = i+1; j < 100; ++j) mat[i][j] = -1;
ans[i] = 6;
}
mat[99][99] = 1; ans[99] = 0;
Gauss();
printf("Case %d: %.6lf\n",ca,ans[0]);
}
return 0;
}