题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4418
题意:给出一个长度为n的序列,编号0到n-1。给出起始位置s和终止位置t,求从s到达t需要走的步数的期望。给出m,每次可以走1到m中的一步,走i步的概率为pi%(题目保证p1+p2+……+pm=100)。给出一个方向d,d=0表示开始时只能从左向右走,d=1表示开始时只能从右向左走。每次走到0或者n-1的时候都要折回去。不能到达t输出Impossible !
思路:从s开始BFS一次 可以判断是否可以到达t。设f(i)表示从i到达t的期望,设从x走下一步可以到达的位置为x1,x2,……xm,那么
f(x)=sigama((f(xi)+i)*pi),设sum=sigama(i*pi),则上式整理得:f(x1)*(-p1)+f(x2)*(-p2)+ ……+f(x)=sum,因此可建立方程组求解。
至此仍有两个问题未解决:
(1)如何解决到达两端后可以折回?比如n=6,s=2,t=4,d=0,我们将n个变成(n-1)*2=10,即0,1,2,3,4,5,6,7,8,9,x和n-x是同一个,这样从2
开始走,走到4和6其实都等于走到4;
(2)对于走不到的点应该怎么列方程呢?对于走不到的期望显然是正无穷。所以只要使等式等于INF即可。
double p[N],a[N][N],ans[N],sum;
int n,m;
int s,t,d,visit[N];
int sgn(double x)
{
if(x>1e-10) return 1;
if(x<-1e-10) return -1;
return 0;
}
void Gauss()
{
int i,j,k,t;
double temp;
for(i=0,j=0;i<n&&j<n;i++,j++)
{
for(k=j;k<n;k++) if(sgn(a[k][i])) break;
for(t=0;t<=n;t++) swap(a[k][i],a[j][i]);
for(t=0;t<n;t++) if(t!=j&&sgn(a[t][i]))
{
temp=a[t][i]/a[j][i];
for(k=0;k<=n;k++) a[t][k]-=a[j][k]*temp;
}
}
FOR0(i,n) ans[i]=a[i][n]/a[i][i];
printf("%.2lf\n",ans[s]);
}
int BFS()
{
queue<int> Q;
Q.push(s); clr(visit,0); visit[s]=1;
int i,u,v;
while(!Q.empty())
{
u=Q.front();
Q.pop();
for(i=1;i<=m;i++)
{
v=(u+i)%n;
if(sgn(p[i])&&!visit[v]) visit[v]=1,Q.push(v);
}
}
return visit[t]||visit[(n-t)%n];
}
void build()
{
clr(a,0);
int i,j;
FOR0(i,n)
{
a[i][i]=1;
if(!visit[i]) a[i][n]=dinf;
if(i==t||i==(n-t)%n) a[i][n]=0;
else
{
a[i][n]=sum;
for(j=1;j<=m;j++) a[i][(i+j)%n]-=p[j];
}
}
}
int main()
{
rush()
{
RD(n,m); RD(t,s); RD(d);
int i;
sum=0;
FOR1(i,m) RD(p[i]),p[i]/=100,sum+=p[i]*i;
n=(n-1)<<1;
if(d==1) s=(n-s)%n;
if(s==t) puts("0.00");
else if(!BFS()) puts("Impossible !");
else build(),Gauss();
}
return 0;
}