拋硬幣(機率dp)

cn是大帅哥886發表於2024-08-16
https://www.luogu.com.cn/problem/AT_dp_i
第1題 拋硬幣 檢視測評資料資訊

有n個質地不均勻的硬幣,拋第i枚硬幣器正面朝上的機率是p[i],反面朝上的機率是1-p[i],扔完n個硬幣後,求正面朝上的個數大於反面朝上的機率是多少。結果保留三位小數

輸入格式

第一行一個整數n,表示有n枚硬幣

第二行n個實數,表示第i個數的正面朝上的機率

1<=n<=2999,0<p[i]<1

輸出格式

一個實數

輸入/輸出例子1

輸入:

3

0.30 0.60 0.80

輸出:

0.612

輸入/輸出例子2

輸入:

1

0.50

輸出:

0.500

樣例解釋

看到機率,就往dp方面想

寫dp前可以先看範圍

這題O(n^2),不難想到第一維肯定是對於前i個硬幣。

狀態要轉個彎,直接求正面>反面比較難算,我們可以求出正面數量,用n-正面得出反面,就可以比較了

狀態就出來了,f[i][j]: 考慮前i個硬幣,有j個正面朝上的機率

轉移:按照第i個硬幣正反討論:
1.正: f[i-1][j-1]*p[i]
2.反: f[i-1][j]*(1-p[i])

答案:
f[n,n/2+1~n]

這題類似揹包,就是選和不選

#include <bits/stdc++.h>
using namespace std;
const int N=3010;

int n;
double a[N], f[N][N], ans=0;
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%lf", &a[i]);
	
	f[0][0]=1;
	for (int i=1; i<=n; i++)
		for (int j=0; j<=i; j++)
		{
			if (j>=1) f[i][j]=f[i-1][j-1]*a[i];
			f[i][j]+=f[i-1][j]*(1.0-a[i]);
		}
			
/*	
	for (int i=1; i<=n; i++)
	{
		for (int j=1; j<=i; j++) printf("%.3lf ", f[i][j]);
		printf("\n");
	}*/
		
	for (int i=n/2+1; i<=n; i++) ans+=f[n][i];
	printf("%.3lf", ans);
	return 0;
}

  

相關文章