1. 程式人生 > >2018.10.13 bzoj4008: [HNOI2015]亞瑟王(概率dp)

2018.10.13 bzoj4008: [HNOI2015]亞瑟王(概率dp)

傳送門 馬上2點考初賽了,心裡有點小緊張。 做道概率dp壓壓驚吧。 話說這題最開始想錯了。 最開始的方法是考慮f[i][j]f[i][j]表示第ii輪出牌為jj的概率。 然後用第ii11~j1j-1都不選的概率與前i1i-1輪都不選jj的概率轉移。 但這樣是錯的。 因為兩個轉移的量是有交集的。 因此需要換一種狀態定義方式。 我們考慮f[i][j]f[i][j]表示前ii張出了jj張的概率(注意是針對所有輪加起來)。 然後轉移就很easyeasy了。 程式碼:

#include<bits/stdc++.h>
#define
db double
using namespace std; int n,r,T; db mul[250][250],f[250][250],d[250],p[250],psum[250],ans; inline void init(){ for(int i=1;i<=n;++i){ mul[i][0]=1; for(int j=1;j<=r;++j)mul[i][j]=mul[i][j-1]*(1-p[i]); } } int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&n,&r),ans=0; for
(int i=1;i<=n;++i)scanf("%lf%lf",&p[i],&d[i]); memset(f,0,sizeof(f)),memset(psum,0,sizeof(psum)),init(); f[1][0]=mul[1][r],f[1][1]=psum[1]=1-f[1][0]; for(int i=2;i<=n;++i){ for(int j=0;j<=r;++j){ psum[i]+=f[i-1][j]*(1-mul[i][r-j]),f[i][j]+=f[i-1][j]*mul[i][r-j]; if(j)f[i]
[j]+=f[i-1][j-1]*(1-mul[i][r-j+1]); } } for(int i=1;i<=n;++i)ans+=psum[i]*d[i]; printf("%.10lf\n",ans); } return 0; }