codeforces284 div1 B:概率dp

蛋疼的期末。。好久没有A题了,,惭愧啊

昨晚打起精神准备做cf 结果竟然忘记注册了。。拿学长号看了看题,今早起来补了一道dp

题目大意:

有n首歌,你需要边听边猜

对于第 i 首歌 每听一分钟你猜出它的概率为p[i],同时在听这个歌t[i] 分钟时,你一定能猜出来

猜完当前歌曲 下一分钟开始听下一首歌

给定总时间 T 求猜出歌曲的期望。。

 

题解:
这个题的算法我想的还是很快的

开两个数组

dp[i][j]表示第 i 分钟还在听第 j 首歌的概率,这样如果没有 t[i]的限制就可以很容易的写出dp方程了,

加上t[i]以后怎么做呢,我的做法是再维护一个数组

q[i][j]表示在第 i 分钟将要开始听第 j 首歌的概率

显然在 i 时间 听 j 歌曲的 第 t[i] 分钟的概率即为 q[i-t[i]] * (1-p[j])^(t[i]-1) (前t[i]-1分钟都没有猜出来)

由于概率是线性叠加起来的。所以在转移的时候直接把这一部分单独考虑就可以了

 

吐槽:

我爆了一次内存,然后换成滚动数组过了。。。听说某金牌爷比赛最后一刻写完 爆内存改完没来得及交

904ms飘过。。。听说一堆红名爷、final爷tle了,果然上天有时候还是会稍微眷顾一下弱逼的

比赛的时候过了这题竟然可以黄。。。早知道就用学长的号交一发了。。

 

代码:

#include <iostream>

#include <stdio.h>

#include<string.h>

#include<algorithm>

#include<string>

#include<ctype.h>

#include<math.h>

#include<queue>

using namespace std;

#define MAXN 10000

const double eps=0.0000000000000001;

double dp[2][5010];

double p[5010];

long long t[5010];

double q[5010][5010];

int main()

{

    long long n,k;

    cin>>n>>k;

    for(int i=0;i<n;i++)

    {

        cin>>p[i]>>t[i];

        p[i]/=100;

    }

    dp[0][0]=1;

    q[0][0]=1;

    for(int i=1;i<=k;i++)

    {

        for(int j=0;j<n;j++)

        {

            if(dp[(i-1)%2][j]<eps)

                continue;

            double tmp;

            if(i>=t[j])

            {

                tmp=q[i-t[j]][j]*pow(1.0-p[j],(double)t[j]-1);

            }

            else

                tmp=0;

            dp[i%2][j]+=(dp[(i-1)%2][j]-tmp)*(1-p[j]);

            dp[i%2][j+1]+=(dp[(i-1)%2][j]-tmp)*p[j]+tmp;

            q[i][j+1]+=(dp[(i-1)%2][j]-tmp)*p[j]+tmp;

            dp[(i-1)%2][j]=0;

        }

        dp[i%2][n]+=dp[(i-1)%2][n];

        dp[(i-1)%2][n]=0;

    }

    double ans=0;

    for(int i=0;i<=n;i++)

    {

        ans+=dp[k%2][i]*i;

    }

    printf("%.7f\n",ans);

    return 0;

}

 

你可能感兴趣的:(codeforces)