[BZOJ3640]JC的小苹果(概率dp+高斯消元)

题目描述

传送门

题解

设f(i,j)表示血量为i,走到j时的概率
一个比较显然的式子是 f(i,j)=(j,v)Ef(i+a(j),v)d(v)
但是有一个问题就是如果a(j)=0的话f(i+a(j))实际上还没有推出来,和f(i)是同一层的
那么就需要用高斯消元来解
而如果每一层都解一遍高斯消元的话时间是 O(n3hp)
可以发现对于每一层来说消元所得的上三角矩阵是一样的,不同的只是常数项
那么每一次将常数项重新赋值然后进行回代就可以了
时间复杂度 O(n2h+2m)
需要注意的几点:
消上三角矩阵的时候要记录消元的过程,每一次常数项要做一遍
本题有重边和自环,所以d(i)有些时候是不同的需要注意一下

代码

#include
#include
#include
#include
using namespace std;
#define N 155
#define M 10005

const double eps=1e-12;
int dcmp(double x)
{
    if (x<=eps&&x>=-eps) return 0;
    return (x>0)?1:-1;
}
int n,m,hp,x,y;
int val[N],cnt[N];
int tot,point[N],nxt[M],v[M];
double ans;
double d[N],f[M][N],a[N][N],b[N];
struct data{int i;double t;}eli[N][N];

void add(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void init()
{
    for (int i=1;i<=n;++i)
    {
        for (int j=i+1;j<=n;++j)
            if (dcmp(a[j][i]))
            {
                double t=a[j][i]/a[i][i];
                for (int k=1;k<=n;++k) a[j][k]-=a[i][k]*t;
                eli[j][++cnt[j]].i=i,eli[j][cnt[j]].t=t;
            }
    }
}
void gauss(int id)
{
    for (int i=n;i>=1;--i)
    {
        for (int j=i+1;j<=n;++j) b[i]-=a[i][j]*f[id][j];
        f[id][i]=b[i]/a[i][i];
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&hp);
    for (int i=1;i<=n;++i) scanf("%d",&val[i]);
    for (int i=1;i<=m;++i)
    {
        scanf("%d%d",&x,&y);
        d[x]+=1.0;add(x,y);
        if (x!=y) d[y]+=1.0,add(y,x);
    }
    for (int i=1;i<=n;++i)
    {
        a[i][i]=1.0;
        if (val[i]) continue;
        for (int j=point[i];j;j=nxt[j])
            if (v[j]!=n) a[i][v[j]]-=1/d[v[j]];
    }
    init();
    for (int i=hp;i>=1;--i)
    {
        memset(b,0,sizeof(b));
        if (i==hp) b[1]+=1.0;
        for (int j=1;j<=n;++j)
            if (val[j]&&i+val[j]<=hp)
            {
                for (int k=point[j];k;k=nxt[k])
                    if (v[k]!=n) b[j]+=f[i+val[j]][v[k]]*1/d[v[k]];
            }   
        for (int j=1;j<=n;++j)
            for (int k=1;k<=cnt[j];++k)
                b[j]-=b[eli[j][k].i]*eli[j][k].t;
        gauss(i);
    }
    for (int i=1;i<=hp;++i) ans+=f[i][n];
    printf("%.8lf\n",ans);
}

你可能感兴趣的:(题解,dp,概率期望,高斯消元)