codeforces 498B. Name That Tune (概率与期望DP+快速幂)

题目描述

传送门

题目大意:有n首歌,顺序播放。只有第i首歌被辨认出,才能播放第i+1首歌。对于每首歌有两个值pi,ti。表示在播放1..ti-1的时间内有pi/100的概率猜出这首歌,如果播放了ti分钟,那么一定可以辨认出。问播放时间为T,辨认出歌的数量的期望。

题解

刚开始写了一个 O(nT2) 的暴力DP
这里简单的说一下思路:
f[i][j] 表示第i首歌在第j秒被认出的概率
f[i][j]=k=1t[i]1f[i1][jk]p[i](1p[i])k1
f[i][j]+=f[i][jt[i]](1p[i])t[i]
但是这样算的时候还有一部分概率遗漏了,就是这首歌听到一半,到时间了,这首歌没有辨别出来,那么这一部分的概率应该加给 f[i1][m]
j=m,k<t[i] f[i1][m]+=f[i1][mk](1p[i])k
ans=i=1n1f[i][m]i+i=1mf[n][i]n

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 5003
using namespace std;
double p[N],f[N][N];
int n,m,t[N];
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) {
        scanf("%lf%d",&p[i],&t[i]);
        p[i]/=100.0;
    }
    f[0][0]=1;
    for (int i=1;i<=n;i++)
     for (int j=m;j>=0;j--) {
        double q=1; int l;
        for (l=1;l<t[i];l++) {
            if (j-l<0) break;
            f[i][j]+=f[i-1][j-l]*p[i]*q;
            q*=(1.0-p[i]);
            if (j==m) f[i-1][m]+=f[i-1][j-l]*q;
         }
        if (j>=t[i]) f[i][j]+=f[i-1][j-t[i]]*q;
        //if (l<t[i]) f[i-1][j]+=f[i-1][j-l]*q;
        //cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
     }
    double ans=0;
    for (int i=1;i<=n;i++)
     ans+=i*f[i][m];
    for (int i=1;i<=m-1;i++) ans+=n*f[n][i];
    printf("%.9lf\n",ans);
}


这种思路改成两层循环有一定的难度,而且常数会稍微大一点,不过应该可以做。。。。
我们现在换一种比较好想的思路。
f[i][j] 表示第j秒该听第i首歌的概率,即已经听出第i-1首歌的概率。
f[i][j+1]+=f[i][j](1p[i])
f[i+1][j+1]+=f[i][j]p[i]
这样子有一种情况是错误的,就是 f[i][j] 经过 t[i] 的时间会转移到 f[i][j+t[i]]
但是实际上应该转移到的是 f[i+1][j+t[i]]
那么这种情况特殊算一下就可以了。
ans=i=2nf[i][m](i1)+i=1mf[n+1][i]n

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 5003
using namespace std;
double p[N],f[N][N],g[N];
int n,m,t[N];
double quickpow(double num,int x)
{
    double ans=1; double base=num;
    while (x) {
        if (x&1) ans=base*ans;
        x>>=1;
        base=base*base;
    }
    return ans;
}
int main()
{
    freopen("a.in","r",stdin);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) {
        scanf("%lf%d",&p[i],&t[i]);
        p[i]/=100.0;
    }
    memset(f,0,sizeof(f));
    f[1][0]=1;//f[i][j]表示第j秒结束改听第i首歌 。 
    for (int i=1;i<=n;i++) {
     for (int j=0;j<=m;j++) g[j]=f[i][j];
     for (int j=0;j<=m;j++) {
        f[i][j+1]+=f[i][j]*(1.0-p[i]);
        f[i+1][j+1]+=f[i][j]*p[i];
        if (j+t[i]<=m) {
            double q=quickpow(1.0-p[i],t[i]);
            f[i][j+t[i]]-=q*g[j];
            f[i+1][j+t[i]]+=q*g[j];
         }
     }
    }
    double ans=0;
    for (int i=2;i<=n;i++) ans+=f[i][m]*(i-1.0);
    for (int i=1;i<=m;i++) ans+=f[n+1][i]*n;
    printf("%.9lf\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值