概率DP+高斯消元
令
fi,j
表示到
i
点,还剩
如果 ai>0 , fi,j=∑i→kfk,j+ai 直接转移就好了
如果 ai=0 ,就要把 ai=0 的点构成联通块提出来高斯消元。
这样是 O(hp×n3) 的,发现树的形态是不变的,那么每次消元的矩阵除了常数项都不会变。
那么常数项可以用一个多项式代替,消出来的结果用多项式表示,DP的时候直接把常数项带进去就可以了。
消元复杂度是 O(n3) 的,DP转移就是 O(hp×n2) 的
注意到了n点后就不会走了……因为这个样例都搞了好久……
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int N=160;
int n,m,hp,cnt,G[N],a[N],d[N],vis[N];
double *A[N],B[N][N],t[N*N],f[N][10010],v[N];
struct edge{
int t,nx;
}E[10010];
inline void Insert(int x,int y){
E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt; d[x]++;
if(x==y) return ;
E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt; d[y]++;
}
inline void Gauss(){
for(int i=1;i<=n;i++){
int t;
for(int j=i;j<=n;j++)
if(fabs(B[j][i])>1e-7){ t=j; break; }
if(t!=i){
for(int j=1;j<=n;j++) swap(B[i][j],B[t][j]);
swap(A[i],A[t]);
}
for(int j=1;j<=n;j++){
if(j==i) continue;
B[i][j]/=B[i][i];
}
for(int j=1;j<=n;j++) A[i][j]/=B[i][i];
B[i][i]=1;
for(int j=1;j<=n;j++)
if(fabs(B[j][i])>1e-7 && j!=i){
double t=B[j][i];
for(int k=1;k<=n;k++) B[j][k]-=t*B[i][k];
for(int k=1;k<=n;k++) A[j][k]-=t*A[i][k];
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&hp);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),A[i]=t+(i-1)*n+1;
for(int i=1,x,y;i<=m;i++)
scanf("%d%d",&x,&y),Insert(x,y);
memset(t,0,sizeof(t)); memset(B,0,sizeof(B));
f[1][hp]=1;
for(int i=1;i<=n;i++)
if(a[i]!=0) A[i][i]=1,B[i][i]=1;
else{
A[i][i]=1; B[i][i]=1;
for(int j=G[i];j;j=E[j].nx)
if(a[E[j].t]==0 && E[j].t!=n) B[i][E[j].t]-=1.0/d[E[j].t];
}
Gauss();
for(int j=hp;j;j--){
for(int i=1;i<=n;i++){
if(!a[i] || a[i]+j>hp) continue;
for(int k=G[i];k;k=E[k].nx)
if(E[k].t!=n) f[i][j]+=1.0/d[E[k].t]*f[E[k].t][j+a[i]];
}
for(int i=1;i<=n;i++){
v[i]=f[i][j];
if(a[i]) continue;
f[i][j]=0;
for(int k=G[i];k;k=E[k].nx)
if(a[E[k].t] && E[k].t!=n) v[i]+=1.0/d[E[k].t]*f[E[k].t][j];
}
for(int i=1;i<=n;i++){
if(a[i]) continue;
for(int k=1;k<=n;k++)
f[i][j]+=v[k]*A[i][k];
}
}
double ans=0;
for(int i=1;i<=hp;i++) ans+=f[n][i];
printf("%.8lf\n",ans);
return 0;
}