题目描述
传送门
题目大意:有n首歌,顺序播放。只有第i首歌被辨认出,才能播放第i+1首歌。对于每首歌有两个值pi,ti。表示在播放1..ti-1的时间内有pi/100的概率猜出这首歌,如果播放了ti分钟,那么一定可以辨认出。问播放时间为T,辨认出歌的数量的期望。
题解
刚开始写了一个
O(n∗T2)
的暴力DP
这里简单的说一下思路:
f[i][j]
表示第i首歌在第j秒被认出的概率
f[i][j]=∑k=1t[i]−1f[i−1][j−k]∗p[i]∗(1−p[i])k−1
f[i][j]+=f[i][j−t[i]]∗(1−p[i])t[i]
但是这样算的时候还有一部分概率遗漏了,就是这首歌听到一半,到时间了,这首歌没有辨别出来,那么这一部分的概率应该加给
f[i−1][m]
当
j=m,k<t[i]
时
f[i−1][m]+=f[i−1][m−k]∗(1−p[i])k
ans=∑i=1n−1f[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]∗(1−p[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]∗(i−1)+∑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);
}