概率DP[NOIP2016D2T3换教室]

YZOJ


概率DP最现实的体现就是数学期望的求解。

1.数学期望的定义:
数学期望是 试验中每次可能结果的概率乘以其结果 的总和,反映了随机变量平均取值的大小。

2.离散型随机变量的数学期望。
当随机变量有限或无限但有一定次序,那么称为离散型随机变量。
离散型随机变量的数学期望定义为:
Ex=ni=1xip(xi) , 集合 { x1,x2...xk } 为 x 的取值范围。

这是OI中常涉及到的一种。还有随机变量还有连续型,但涉及到过多积分的知识,我还太弱。连续型随机变量就是变量的取值是连续的,比如 x(100,100)
不管最怎么样,通过对随机变量的分布特征(取值范围)进行分类,我们可以针对的求解一类问题。

3.性质。
从公式里可以看出,离散型随机变量的期望是线性的(连续性的也是)。所以:
E(ax+b)=aE(x)+E(b)
所以一个期望的组成部分实际上是互不影响的。假若有一棵二叉树,每一条边都有权值。一个人从根节点开始随机选择左右子节点作为路径,那么路径的总权值的求解就转化为到达每一个点概率的求解。最后答案就是:
E=Si=12dep(i)vi 在这个例子中,选择具有时间性,某一事件的概率会是由前面事件的概率推导出来的。


本题很重要的性质是,每次选择的概率互不影响,不用求前缀和。

有两种记状态的方法:记当前到达的教室或当前是否申请。
如果选择第一种方法的话,当前状态表示的是做到第i节课,已经申请了j次,当前在1/2教室的期望。我们发现到达下一个点的路径期望不能单纯的乘上k[i]而还要考虑前面的点到当前点的概率。这样实在不好写。
后一种方法,当前的状态表示的是是做到第i节课,已经申诉了j次,当前在是否申请的期望。这个状态中是这个人上完本节课所需要走过的路径期望,是在两个教室的状态叠加。由于记录了当前是否申诉,根据前面的那个性质,这个人在哪一间教室的概率是确定的,所以转移的时候,就可以很方便的讨论是从哪一件教室转移来的了。

我们发现,这道题之所以选择第二种记状态的方法,是因为一条路径考虑了两节课的选择情况。

在【换教室】中
C3 D3

C2 D2

C1 D1

有四种路径可以选择。其中每一种情况的概率都要考虑到选择到每一个点的概率。如路径D2->D3的概率,为k[1]*k[2]考虑了从1选2和从2选3的概率。
所以floyd+概率DP+滚动数组优化,就可以在时间复杂度 O(n3+mn) , 空间复杂度 O(n) 的情况下快速求解了。


#include<cstdio>
#include<algorithm>
#include<cstring>
#define R register
#define dd double
#define cmin(_a,_b)(_a)>(_b)?(_a)=(_b):0
#define dmin(_a,_b)(_a)<(_b)?(_a):(_b)
using namespace std;
int N,M,V,E;
int c[2010],d[2010],dis[310][310];
dd kk[2010],f[2][2010][2],ans=1<<30;

int main()
{
    R int a1,b1,c1,i,j,k,tmp;
    R dd tmp1,tmp2,tmp3,tmp4,tmp5,tmp6;

    scanf("%d%d%d%d",&N,&M,&V,&E);
    if(N==1){printf("%.2lf",0.000);return 0;}
    for(i=1;i<=N;i++)scanf("%d",&c[i]);
    for(i=1;i<=N;i++)scanf("%d",&d[i]);
    for(i=1;i<=N;i++)scanf("%lf",&kk[i]);
    memset(dis,63,sizeof(dis));
    for(i=1;i<=V;i++)dis[i][i]=0;
    for(i=1;i<=E;i++){
        scanf("%d%d%d",&a1,&b1,&c1);
        dis[a1][b1]=dis[b1][a1]=dmin(dis[a1][b1],c1);
    }

    for(k=1;k<=V;k++)
        for(i=1;i<=V;i++)
            for(j=1;j<=V;j++)
            {
                tmp=dis[i][k]+dis[k][j];
                if(tmp<dis[i][j])dis[i][j]=dis[j][i]=tmp;
            }

    memset(f,66,sizeof(f));
    f[1][0][0]=0,f[1][1][1]=0;
    for(i=2,k=0;i<=N;++i,k=i%2)
        for(j=0;j<=i&&j<=M;j++)
        {
            tmp1=f[k^1][j][0]+dis[c[i-1]][c[i]];
            tmp2=f[k^1][j][1]+dis[c[i-1]][c[i]]*(1-kk[i-1])+dis[d[i-1]][c[i]]*kk[i-1];
            f[k][j][0]=dmin(tmp1,tmp2);
            if(i==N)cmin(ans,f[k][j][0]);

            if(!j)continue;
            tmp1=f[k^1][j-1][0]+dis[c[i-1]][c[i]]*(1-kk[i])+dis[c[i-1]][d[i]]*kk[i];
            tmp2=f[k^1][j-1][1]+dis[c[i-1]][c[i]]*(1-kk[i-1])*(1-kk[i])+dis[c[i-1]][d[i]]*kk[i]*(1-kk[i-1])
            +dis[d[i-1]][c[i]]*kk[i-1]*(1-kk[i])+dis[d[i-1]][d[i]]*kk[i-1]*kk[i];
            f[k][j][1]=dmin(tmp1,tmp2);
            if(i==N)cmin(ans,f[k][j][1]);
        }
    printf("%.2lf",ans);
    return 0;           
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值