题目描述:
HDU4418题目传送门
给出一个数轴
[
0
,
n
)
[0,n)
[0,n),有一个起点和一个终点,某个人一次可以
走1,2,3……m步,走k步的概率为
p
k
p_k
pk,初始有一个方向(左或右),
走到头则掉头返回,问到达终点的期望步数为多少。
题目分析:
明显的一个高斯消元题。
对于可以掉头走我们只需要把0,1,2,…(n-1)序列变成 0,1,2…(n-2),(n-1),(n-2)…2,1,令N=2*(n-1),那么从i点走j步后到达的位置就是(i+j)%N
如果最初的方向为向左,那么令X=N-X,就仍然是向右走了。
设
f
[
i
]
f[i]
f[i]为从
i
i
i走到终点
Y
Y
Y的期望步数,那么
f
[
i
]
=
∑
j
=
1
m
p
[
j
]
∗
f
[
(
i
+
j
)
%
N
]
f[i]=\sum_{j=1}^mp[j]*f[(i+j)\%N]
f[i]=j=1∑mp[j]∗f[(i+j)%N]
终点
f
[
Y
]
=
f
[
N
−
Y
]
=
0
f[Y]=f[N-Y]=0
f[Y]=f[N−Y]=0
然而仅仅这样会WA到自闭。
因为我太菜了忘了判无解,我太菜了想当然地以为最后只要解出来系数不为0就有解。。。
如果起点是奇数点终点是偶数点,走两步的概率是100%,那么肯定到不了,方程解出来会有自由元。
于是我们需要事先bfs一波,看起点是否能够走到终点。
其实结合意义想一想只要起点能够走到终点,不能到达终点的点就不会出现在起点到终点的路径中,所以除了bfs其他地方都不会导致impossible,最后放心一除,起点系数不会为一(随意揣测仅供参考可能是错的但是交上去确实能过)。
Code:
#include <bits/stdc++.h>
#define maxn 505
#define LL long long
using namespace std;
const double eps = 1e-8, inf = 1e20;
int T,n,m,X,Y,D;
bool vis[maxn];
double p[maxn],a[maxn][maxn];
inline bool no(double x){return fabs(x)<eps;}
bool Gauss(int N){
for(int i=0;i<N;i++){
if(no(a[i][i]))
for(int j=i+1;j<N;j++)
if(!no(a[j][i])) {swap(a[i],a[j]);break;}
if(no(a[i][i])) continue;
for(int j=0;j<N;j++) if(i!=j&&!no(a[j][i])){
double t = a[j][i]/a[i][i];
for(int k=i;k<=N;k++) a[j][k]-=a[i][k]*t;
}
}
}
queue<int>q;
bool bfs(){
q.push(X),vis[X]=1;
while(!q.empty()){
int i=q.front();q.pop();
for(int j=1,v;j<=m;j++) if(p[j]>eps&&!vis[v=(i+j)%n]) vis[v]=1,q.push(v);
}
for(int i=0;i<n;i++) if(!vis[i]) a[i][i]=1,a[i][n]=inf;
return vis[Y]||vis[n-Y];
}
int main()
{
scanf("%d",&T);
while(T--){
memset(a,0,sizeof a);
memset(vis,0,sizeof vis);
scanf("%d%d%d%d%d",&n,&m,&Y,&X,&D);
for(int i=1;i<=m;i++) scanf("%lf",&p[i]),p[i]/=100;
if(X==Y) {puts("0.00");continue;}
n=2*(n-1);
if(D==1) X=n-X;
if(!bfs()) {puts("Impossible !");continue;}
for(int i=0;i<n;i++) if(vis[i]){
if(i==Y||i==n-Y) a[i][i]=1,a[i][n]=0;
else{
a[i][i]=1;
for(int j=1;j<=m;j++) a[i][(i+j)%n]+=-p[j],a[i][n]+=p[j]*j;
}
}
Gauss(n);
printf("%.2f\n",a[X][n]/a[X][X]);
}
}