期望与概率dp

概率与期望dp

定义:

概率:事件A发生的可能性,计作P(A)

期望:事件A结果的平均大小,记住E(x)

? E(x)=每种结果的大小与其概率的乘积的和

注意计算概率时需要考虑是否要用容斥原理

期望dp时注意有时要用倒序枚举

其实本质和其他的dp没什么区别

例题

概率充电器

题面

题意:n个充电元件由n-1条导线连通,每个充电原件自身是否直接充电以及每条导线是否导电都由概率决定,求进入充电状态的元件个数的期望

1<=n<=500000

树形换根概率dp,注意使用容斥原理

第一遍dfs:计算出f[i]表示由子树i通电的概率

第二遍dfs:计算出从父亲节点来电的概率,再用容斥原理加在一起

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
#define maxn 500100
int n;
int fir[maxn],nxt[maxn*2],vv[maxn*2];
double edge[maxn*2],q[maxn];
int tot=0;
double ans=0;
void add(int u,int v,double w)
{
    nxt[++tot]=fir[u];
    fir[u]=tot;
    vv[tot]=v;
    edge[tot]=w;
}
double h[maxn];
void dfs1(int u,int fa)
{
    h[u]=q[u];
    for(int i=fir[u];i;i=nxt[i])
    {
        int v=vv[i];
        if(v==fa)continue;
        dfs1(v,u);
        h[u]=h[u]+h[v]*edge[i]-h[u]*h[v]*edge[i];
    }
}
void dfs2(int u,int fa)
{
    for(int i=fir[u];i;i=nxt[i])
    {
        int v=vv[i];
        if(v==fa)continue;
        if(fabs(1-h[v]*edge[i])>(1e-7))
        {
            double t=(h[u]-h[v]*edge[i])/(1-h[v]*edge[i]);
            h[v]=h[v]+t*edge[i]-h[v]*t*edge[i];
            ans+=h[v];
        }else ans+=1;
        dfs2(v,u);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int a,b;double p;scanf("%d%d%lf",&a,&b,&p);
        add(a,b,p/100);add(b,a,p/100);
    }
    for(int i=1;i<=n;i++)scanf("%lf",&q[i]),q[i]/=100;
    dfs1(1,0);
    ans=h[1];
    dfs2(1,0);
    printf("%0.6lf",ans);
    return 0;
}

换教室

题目

题意:小A的学校可以视为一个v个点的无向图,他有n门课程要按顺序上课,其中第i门课程要在节点ai进行,但还有一个备选地点bi。现在小A有m个申请机会,若申请第i门课,那么将有ki的概率使课程搬到bi进行。每门课最多申请一次,m次机会不必全部用完。他如何申请才能最小化在上课地点间移动的距离的期望值。求该期望值。
v<=300,n,m<=200

先用floyd求出任意两点的距离

再令f[i][j][0/1]表示前i节课申请m次且第i节课申请/不申请的最小期望值

注意double类型需要手动赋初值

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n,m,v,e;
double f[2010][2010],dp[2010][2010][2],k[2010];
int c[2010],d[2010];
double mymin(double x,double y)
{
    return x<y?x:y;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&v,&e);
    for(int i=1;i<=n;i++)scanf("%d",&c[i]);
    for(int i=1;i<=n;i++)scanf("%d",&d[i]);
    for(int i=1;i<=n;i++)scanf("%lf",&k[i]);
    for(int i=1;i<=v;i++)for(int j=1;j<=v;j++)f[i][j]=1e9;
    for(int i=1;i<=v;i++)f[i][i]=0;
    for(int i=1;i<=e;i++)
    {
        int a,b;double w;scanf("%d%d%lf",&a,&b,&w);
        f[a][b]=f[b][a]=min(f[a][b],w);
    }
    for(int kk=1;kk<=v;kk++)
    {
        for(int i=1;i<=v;i++)
        {
            for(int j=1;j<=v;j++)
            {
                f[i][j]=f[j][i]=min(f[i][j],f[i][kk]+f[kk][j]);
            }
        }
    }
    for(int i=1;i<=n;i++)for(int j=0;j<=m;j++)
    {
        dp[i][j][0]=1e9;dp[i][j][1]=1e9;
    }
    dp[1][0][0]=dp[1][1][1]=0;
    for(int i=2;i<=n;i++)
    {
        for(int j=0;j<=min(m,i);j++)
        {
            dp[i][j][0]=mymin(dp[i-1][j][0]+f[c[i]][c[i-1]],dp[i-1][j][1]+k[i-1]*f[c[i]][d[i-1]]+(1-k[i-1])*f[c[i]][c[i-1]]);
            if(j!=0)
            dp[i][j][1]=mymin(dp[i-1][j-1][0]+f[c[i-1]][c[i]]*(1-k[i])+f[c[i-1]][d[i]]*k[i],dp[i-1][j-1][1]+f[c[i-1]][c[i]]*(1-k[i])*(1-k[i-1])+f[c[i-1]][d[i]]*(1-k[i-1])*k[i]+f[d[i-1]][c[i]]*k[i-1]*(1-k[i])+f[d[i-1]][d[i]]*k[i-1]*k[i]);
        }
    }
    double ans=1e9;
    for(int i=0;i<=m;i++)
    {
        ans=mymin(ans,dp[n][i][0]);ans=mymin(ans,dp[n][i][1]);
    }
    printf("%0.2lf",ans);
    return 0;
}

奖励关

[题目]

题意:有n轮游戏和m种宝物,每种宝物有分数Pi(可以为负),每轮游戏会等概率抛出一种宝物,你可以选择吃或不吃。第i种宝物还有一个限制集合Si,表示只有在Si中的宝物都吃过后,才能吃第i种宝物。

1<=n<=100 1<=m<=15

期望状压dp

f[i][S]表示在第1轮到第i?1轮内宝物是否取过的状态为S,第i轮到第n轮的最大期望得分,进行逆推。

采用逆推的原因如下:

如果要从当前状态S0转移到目标状态S1,那么直接f[i+1][s1]+=f[i][s0]/n的转移方法是错误的,因为f[i+1][n]不一定有n种被转移到的方式,有可能因为限制集合的原因,有些状态不能转移到f[i+1][s],但如果采用逆推,那么我们是从一个合法的状态转移到当前状态,就不存在这样的问题,大多数期望dp都可以采用逆推的方法。

那么逆推如何转移呢:

枚举第k种宝物,可以取或不取,

如果能取,f[i][s]+=max(f[i+1][s],f[i+1][s|(1<<(k-1))]+p[k])

如果不能取,f[i][s]+=f[i+1][s]

然后f[i][s]/=n

答案为f[1][0]

#include <iostream>
#include <cstdio>
using namespace std;
int n,k;
double f[110][1<<18],value[110];
int sta[110];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=k;i++)
    {
        scanf("%lf",&value[i]);
        int x;scanf("%d",&x);
        while(x!=0)
        {
            sta[i]=sta[i]|(1<<(x-1));
            scanf("%d",&x);
        }
    }
    for(int i=n;i>=1;i--)
    {
        for(int j=0;j<=(1<<k)-1;j++)
        {
            for(int kk=1;kk<=k;kk++)
            {
                if((j|sta[kk])==j)
                {
                    f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(kk-1))]+value[kk]);
                }
                else f[i][j]+=f[i+1][j];
            }
            f[i][j]=(double)f[i][j]*1.0/k;
        }
    }
    printf("%.6lf",f[1][0]);
    return 0;
}

?

原文地址:https://www.cnblogs.com/LianQ/p/11406516.html

时间: 2024-07-29 17:56:47

期望与概率dp的相关文章

POJ2096Collecting Bugs(数学期望,概率DP)

问题: Ivan is fond of collecting. Unlike other people who collect post stamps, coins or other material stuff, he collects software bugs. When Ivan gets a new program, he classifies all possible bugs into n categories. Each day he discovers exactly one

【XSY2518】记忆(memory)(状压dp,概率与期望,概率dp)

题面 Description 你在跟朋友玩一个记忆游戏. 朋友首先给你看了\(n\)个长度相同的串,然后从中等概率随机选择了一个串. 每一轮你可以询问一个位置上的正确字符,如果能够凭借已有的信息确定出朋友所选的串,那么游戏就结束了,你的成绩就是所用的轮数. 由于你实在太笨,不会任何策略,因此你采用一种方法,每次等概率随机询问一个未询问过的位置的字符. 现在你想知道,在这种情况下,你猜出结果所需的期望次数. Input 第\(1\)行包含一个整数 \(n\),表示串的个数. 第 \(2\sim n

HDU 4403(Aeroplane chess ,求期望,概率DP)

Aeroplane chess Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description Hzz loves aeroplane chess very much. The chess map contains N+1 grids labeled from 0 to N. Hzz starts at grid 0. For each step he

HDU 4405 Aeroplane chess (概率DP求期望)

题意:有一个n个点的飞行棋,问从0点掷骰子(1~6)走到n点需要步数的期望 其中有m个跳跃a,b表示走到a点可以直接跳到b点. dp[ i ]表示从i点走到n点的期望,在正常情况下i点可以到走到i+1,i+2,i+3,i+4,i+5,i+6 点且每个点的概率都为1/6 所以dp[i]=(dp[i+1]+dp[i+2]+dp[i+3]+dp[i+4]+dp[i+5]+dp[i+6])/6  + 1(步数加一). 而对于有跳跃的点直接为dp[a]=dp[b]; #include<stdio.h>

HDU4336-Card Collector(概率DP求期望)

Card Collector Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2195    Accepted Submission(s): 1034 Special Judge Problem Description In your childhood, do you crazy for collecting the beautifu

POJ 2096:Collecting Bugs 概率DP求期望

Collecting Bugs 题目连接: http://poj.org/problem?id=2096 题意: Ivan喜欢收集bug,他每天都会找到一个bug,找到的这个bug有一种属性并且属于一个子系统,bug共有n种属性,子系统共有s个 (0<n, s≤1000),求Ivan集齐了n种bug且每个子系统都有bug的期望. 题解: 第一道求期望的题,令dp[i][j]表示系统已经有了i个系统的全部j种bug并且要得到所有bug的天数的期望,因此dp[n][s]=0,而dp[0][0]则是所

ZOJ3329-One Person Game(概率DP求数学期望)

One Person Game Time Limit: 1 Second      Memory Limit: 32768 KB      Special Judge There is a very simple and interesting one-person game. You have 3 dice, namelyDie1, Die2 and Die3. Die1 hasK1 faces. Die2 has K2 faces.Die3 has K3 faces. All the dic

HDU3853-LOOPS(概率DP求期望)

LOOPS Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 125536/65536 K (Java/Others) Total Submission(s): 1864    Accepted Submission(s): 732 Problem Description Akemi Homura is a Mahou Shoujo (Puella Magi/Magical Girl). Homura wants to help h

SGU495 Kids and Prizes 概率DP,期望公式

题目 这题目首先进去以后,没地方提交,第一次做SGU的题目,只能在HUSTOJ上提交了 有n个盒子,里面有礼物的,m个人,每个人拿,拿过以后 把礼物取出来 把盒子放回去,求选中礼物数的期望 其实一开始就假设方程 dp[i]为 第i个人获得礼物的概率,但是状态转移方程不知道该怎么办,想了很久都没有办法, 其实首先边界为dp[1] = 1 第一个上来选的人肯定必中 接下来一个人的 则由两部分组成,dp[i] = (1 - dp[i - 1]) * dp[i - 1] + .........,因为上一