Codeforces Round #284 (Div. 2): D. Name That Tune(概率DP)

题目链接:http://codeforces.com/contest/499/problem/D

题意:有n首曲子,一首一首的放给你听,然后要你猜歌名,对于第i首歌,你前ti-1秒每一秒猜对概率为pi,在第ti秒你必猜对,一旦第i首歌被猜对,立刻播放下一首,求T秒之后你猜对歌曲数量的期望

思路:dp[i][j]表示第i首歌刚好在第j秒时被猜对

有转移dp[i][j] = ∑(dp[i-1][j-k]*p[i]^(k-1)*(1-p[i])(1<=k<=t[i]-1))+dp[i-1][j-t[i]]*p[i]^(t[i]-1)

暴力转移复杂度O(nT²),但求和部分可以前缀和优化,复杂度O(nT)

最后答案就是将所有的dp[i][j]加在一起,为什么是这样算的呢?因为你每转移一次就相当于多猜对一首歌,所以最后就是将所有的转移乘上发生这次转移的概率,即dp[i][j]


#include
#include
using namespace std;
double p[5005], dp[5005][5005];
int t[5005];
double Pow(double x, int k)
{
	double ans = 1;
	while(k)
	{
		if(k%2)
			ans = ans*x;
		x = x*x;
		k /= 2;
	}
	return ans;
}
int main(void)
{
	double now, ans, temp;
	int n, T, i, j, k;
	scanf("%d%d", &n, &T);
	for(i=1;i<=n;i++)
	{
		scanf("%lf%d", &p[i], &t[i]);
		p[i] /= 100;
	}
	dp[0][0] = 1, ans = 0;
	for(i=1;i<=n;i++)
	{
		now = 0;
		temp = Pow(1-p[i], t[i]-1);
		for(j=1;j<=T;j++)
		{
			now *= 1-p[i];
			now += dp[i-1][j-1]*p[i];
			if(j>=t[i])
				now -= dp[i-1][j-t[i]]*temp*p[i];
			dp[i][j] = now;
			if(j>=t[i])
				dp[i][j] += dp[i-1][j-t[i]]*temp;
			ans += dp[i][j];
		}
	}
	printf("%.10f\n", ans);
	return 0;
}


你可能感兴趣的:(#,动态规划)