题意:一个人在数轴上来回走,以pi的概率走i步i∈[1, m],给定n(数轴长度),m,e(终点),s(起点),d(方向),求从s走到e经过的点数期望。
思路:先把n(n>1)个点展开成2*n-2个点,这一步的作用是简化方向这个状态,使得现在所有点在一条直线上,例如原来是01234321,现在展开成了01234567.
设dp[i]为当前在i点走到目标点的期望步数,根据状态转移可以得到若干方程
但是,这些方程里有些是不合法的,所以会导致结果错误,
这是因为,对于一个点来说,可能从起点走不到这个点,但是方程组里我们还是保留了这些方程,这样的话结果计算出来就会错误,所以在开始时我们可以一次bfs将所有能到达的点标记并重新编号,然后计算这些点的状态转移方程。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-9
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
int n, m, Y, X, d;
const int MAXN=220;
double p[MAXN];
double a[MAXN][MAXN],x[MAXN];//方程的左边的矩阵和等式右边的值,求解之后x存的就是结果,行列编号从0开始
int equ,var;//方程数和未知数个数
int num[MAXN];
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(fabs(a[i][col])>fabs(a[max_r][col]))
max_r=i;
if(fabs(a[max_r][col])<eps)return 0;
if(k!=max_r) {
for(j=col;j<var;j++)
swap(a[k][j],a[max_r][j]);
swap(x[k],x[max_r]);
}
x[k]/=a[k][col];
for(j=col+1;j<var;j++)a[k][j]/=a[k][col];
a[k][col]=1;
for(i=0;i<equ;i++)
if(i!=k) {
x[i]-=x[k]*a[i][k];
for(j=col+1;j<var;j++)a[i][j]-=a[k][j]*a[i][col];
a[i][col]=0;
}
}
return 1;
}
int bfs() {
queue<int> q;
q.push(X);
int cnt = 0;
memset(num, -1, sizeof(num));
num[X] = cnt++;
while(!q.empty()) {
int t = q.front(); q.pop();
for(int i = 1; i <= m; i++) {
if(fabs(p[i]) < eps) continue;
int id = (t+i)%n;
if(num[id] == -1) {
num[id] = cnt++;
q.push(id);
}
}
}
return cnt;
}
int main() {
//freopen("input.txt", "r", stdin);
int T; cin >> T;
while(T--) {
int tmp;
scanf("%d%d%d%d%d", &tmp, &m, &Y, &X, &d);
for(int i = 1; i <= m; i++) scanf("%lf", &p[i]), p[i]/=100;
n = 2*tmp - 2;
if(!n) n+=1;
if(d>0) X = n - X;
equ = var = bfs();
if(num[Y]==-1 && num[n-Y]==-1) {
printf("Impossible !\n");
continue;
}
memset(x, 0, sizeof(x));
memset(a, 0, sizeof(a));
for(int i = 0; i < n; i++) {
if(num[i]==-1) continue;
a[num[i]][num[i]] = 1.0;
if(i==Y || i==n-Y) continue;
for(int j = 1; j <= m; j++) {
int id = (i+j) % n;
if(num[id] != -1) {
a[num[i]][num[id]] -= p[j];
x[num[i]] += j*p[j];
}
}
}
if(Gauss()) printf("%.2f\n", x[num[X]]);
else printf("Impossible !\n");
}
return 0;
}