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

来源:互联网 发布:整容 知乎 编辑:程序博客网 时间:2024/06/06 01:43

题目描述

传送门

题目大意:有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 5003using 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 5003using 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);}
0 0
原创粉丝点击