bzoj 3640(期望与DP)

8 篇文章 0 订阅
4 篇文章 0 订阅

3640: JC的小苹果

Time Limit: 15 Sec  Memory Limit: 256 MB
Submit: 332  Solved: 120
[ Submit][ Status][ Discuss]

Description

   让我们继续JC和DZY的故事。

    “你是我的小丫小苹果,怎么爱你都不嫌多!”

    “点亮我生命的火,火火火火火!”

    话说JC历经艰辛来到了城市B,但是由于他的疏忽DZY偷走了他的小苹果!没有小苹果怎么听歌!他发现邪恶的DZY把他的小苹果藏在了一个迷宫里。JC在经历了之前的战斗后他还剩下hp点血。开始JC在1号点,他的小苹果在N号点。DZY在一些点里放了怪兽。当JC每次遇到位置在i的怪兽时他会损失Ai点血。当JC的血小于等于0时他就会被自动弹出迷宫并且再也无法进入。

    但是JC迷路了,他每次只能从当前所在点出发等概率的选择一条道路走。所有道路都是双向的,一共有m条,怪兽无法被杀死。现在JC想知道他找到他的小苹果的概率。

    P.S.大家都知道这个系列是提高组模拟赛,所以这是一道送分题balabala

Input

第一行三个整数表示n,m,hp。接下来一行整数,第i个表示jc到第i个点要损失的血量。保证第1个和n个数为0。接下来m行每行两个整数a,b表示ab间有一条无向边。

Output

    仅一行,表示JC找到他的小苹果的期望概率,保留八位小数。

Sample Input

3 3 2
0 1 0
1 2
1 3
2 3

Sample Output

0.87500000

HINT

对于100%的数据 2<=n<=150,hp<=10000,m<=5000,保证图联通。


解题思路:首先因为血量可以为0,所以这不是个DAG(可直接递推)。

那么就要考虑形成环了,那么可以用高斯消元处理,但是,如果每次

都一次高斯消元的话,会超时,那么可以想到,每次高斯消元的系数

都是不变的,就是说,原来的f[n][n+1],我们把它开一个数组,表示

y[i][j]表示,由j对i的贡献的系数,预处理以后,递推hp,每次就可以用

y数组的这个系数来更新f数组。最后统计答案。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,hp,len;
int to[11000],next[11000],h[11000];
int jc[200];
bool ch[200][200];
long double du[200],x[200][200],y[200][200],f[11000][200],zan[200];
 
inline int read()
{
    char y; int x=0,f=1; y=getchar();
    while(y<'0' || y>'9') {if (y=='-') f=-1; y=getchar();}
    while(y>='0' && y<='9') {x=x*10+int(y)-48; y=getchar();}
    return x*f;
}
 
void insert(int x,int y)
 {
    ++len; to[len]=y; next[len]=h[x]; h[x]=len;
 }
 
void work()
{
    for (int i=1;i<=n-1;++i)
     {
        int u=h[i];
        while (u!=0)
         { 
          if (jc[to[u]]==0) 
           {
            x[to[u]][i]-=1/du[i];
           }
          u=next[u];
         }
     }
    for (int i=1;i<=n;++i) x[i][i]+=1;
    for (int i=1;i<=n;++i) y[i][i]=1;
    for (int i=1;i<=n;++i)
     {
        long double ogg=x[i][i];
        for (int j=1;j<=n;++j) y[i][j]/=ogg;
        for (int j=i;j<=n;++j)
         {
            x[i][j]/=ogg;
         }
        for (int j=i+1;j<=n;++j)
         {
            ogg=x[j][i];
            for (int k=1;k<=n;++k) y[j][k]-=ogg*y[i][k];
            for (int k=i;k<=n;++k) x[j][k]-=ogg*x[i][k];
         }
     }
    for (int i=n;i>=2;--i)
      for (int j=i-1;j>=1;--j)
        {
         long double ogg=x[j][i];
         for (int k=1;k<=n;++k) y[j][k]-=y[i][k]*ogg;    
         x[j][i]=0;
        }
}
 
int main()
{
    n=read(); m=read(); hp=read();
    for (int i=1;i<=n;++i) jc[i]=read();
    memset(ch,false,sizeof(ch));
    for (int i=1;i<=m;++i)
     {
        int x,y; x=read(); y=read();
        if (x!=y)insert(x,y),++du[x]; 
insert(y,x); ++du[y]; 
     }
    work();
    f[hp][1]=1;
    for (int i=hp;i>=1;--i)
     {
        memset(zan,0,sizeof(zan));
        for (int j=1;j<=n;++j)
         {
            for (int k=1;k<=n;++k)
             zan[j]+=f[i][k]*y[j][k];
          }
        for (int j=1;j<=n;++j) f[i][j]=zan[j];
        for (int j=1;j<=n-1;++j) 
          {
            int u=h[j];
            while (u!=0)
             {
                if (jc[to[u]]!=0 && i-jc[to[u]]>0) 
                 {
                    f[i-jc[to[u]]][to[u]]+=f[i][j]/du[j];
                 }
                u=next[u];
             }
          } 
    }
    long double ans=0;
    for (int i=hp;i>=1;--i)
     {
        ans+=f[i][n];
     }
    printf("%.8Lf",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值