题意: 有一个人在一根长度n的线段上来回走,方向是固定的,除非碰到线段的左右端点。他每次走的时候,有p1的概率走1步(即向前走1个点),p2的概率走2步...pm的概率走m步,让你求从点x走到点y的步数的期望值。若不可能走到,输出"Impossible !"。
解法: 对于来回走的处理方式,比如0 1 2 3 4 5,我们可以把他看成 0 1 2 3 4 5 4(6) 3(7) 2(8) 1(9),1和9代表点1,2和8代表点2...这样处理后就可以很容易的确定从当前位置pos走j步后的位置: newpos = (pos+j)mod(2*n-2)。设从点i到y的期望步数为E(i),那么有E(i) = sigma((E(i+j)+j)*p(j)),p(j)为走j步的概率,整理得E(i)-sigma(E(i+j)*p(j)) = sigma(j*p(j))。用广搜确定终点是否可达,然后建立n个方程,高斯消元即可。(关于网传必须只对可达点建立方程,本着不信邪的态度建了n个,AC了。。难道数据水?个人觉得建n个方程是没有问题的。)
/* Created Time: Thursday, October 31, 2013 PM07:18:43 CST */
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
const double eps = 1e-8;
const int M = 210;
int n,m,dv,sv,MOD;
double p[M],g[M][M]; // g[][] : 方程组对应的增广矩阵
bool vis[M];
int sig(double x) { return (x>eps)-(x<-eps); }
double abs(double x) { return sig(x)<0?-x:x; }
bool check() {
memset(vis,0,sizeof(vis));
vis[sv] = true;
queue <int> que;
que.push(sv);
while(!que.empty()) {
int f = que.front(); que.pop();
if (f==dv || f==2*n-2-dv) continue;
for (int i = 1; i <= m; i ++) {
if (sig(p[i])==0) continue;
int e = (f+i)%MOD;
if (vis[e]) continue;
vis[e] = true;
que.push(e);
}
}
return vis[dv] || vis[2*n-2-dv];
}
void Gauss() {
int k,col;
int equ,var;
equ = var = MOD;
for (k=0,col=0; k<equ && col<var; k++,col++) {
// 找到变量x(col)的系数最大行
int maxr = k;
for (int i = k+1; i < equ; i ++)
if (abs(g[i][col])>abs(g[maxr][col]))
maxr = i;
// 若变量x(col)不存在非0系数,则说明x(col)为自由元
if (sig(g[maxr][col])==0) {
k--;
continue;
}
// 将系数最大行与当前行交换(据说这样能避免一些精度问题)
if (maxr!=k)
for (int i = col; i <= var; i ++)
swap(g[k][i],g[maxr][i]);
// 将k+1行到equ-1行的x(col)消去
for (int i = k+1; i < equ; i ++) {
double coe = g[i][col]/g[k][col];
for (int j = col; j <= var; j ++) {
g[i][j] -= g[k][j]*coe;
}
}
}
// 回代
for (k--,col--; k>=0 && col>=0; k--,col--) {
while (sig(g[k][col])==0) col--;
g[k][var] /= g[k][col];
for (int i = k-1; i >= 0; i --) {
g[i][var] -= g[i][col]*g[k][var];
g[i][col] = 0;
}
}
}
void work() {
int tp = 2*n-2-dv;
// 建立增广矩阵
for (int i = 0; i < MOD; i ++) {
g[i][i] = 1;
for (int j = 1; j <= m; j ++) {
g[i][(i+j)%MOD] -= p[j];
g[i][MOD] += p[j]*j;
}
}
for (int i = 0; i <= MOD; i ++)
g[dv][i] = g[tp][i] = 0;
g[dv][dv] = g[tp][tp] = 1;
Gauss();
for (int i = 0; i < MOD; i ++)
if (sig(g[i][sv])!=0) {
printf("%.2f\n",g[i][MOD]);
break;
}
}
int main() {
int cas,d;
scanf("%d",&cas);
while (cas--) {
scanf("%d%d%d%d%d",&n,&m,&dv,&sv,&d);
for (int i = 1; i <= m; i ++) {
scanf("%lf",&p[i]);
p[i] /= 100;
}
// 需要特判起点等于终点的情况,不然WA死,哪怕在解方程后特判0.00都不管用。
if (sv==dv) {
printf("0.00\n");
continue;
}
MOD = 2*n-2;
for (int i = 0; i < MOD; i ++)
for (int j = 0; j <= MOD; j ++)
g[i][j] = 0;
if (d==1) sv = 2*n-sv-2;
if (!check()) {
puts("Impossible !");
}
else
work();
}
return 0;
}
/*
3
19 10 1 17 1
12 4 3 7 31 4 1 0 22 16
20 6 10 7 0
22 36 0 6 21 15
4 2 2 0 -1
0 100
ans :
97.24
52.02
2.00
*/