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

245 篇文章 0 订阅
34 篇文章 0 订阅

题目描述

传送门

题解

设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<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
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);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值