火山喷发 计蒜客16862 NOIP模拟赛 概率DP
Posted JayWang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了火山喷发 计蒜客16862 NOIP模拟赛 概率DP相关的知识,希望对你有一定的参考价值。
题目描述
输入格式
输出格式
数据范围与约定
对于 10% 的数据 N=1。
对于 30% 的数据 N=2。
对于全部数据 N≤4,M≤120 , A_i≤50。
样例输入1
1 2 1样例输出1
0.000000样例输入2
3 15 2 12 2样例输出2
0.001684 0.996632 0.001684题目来源
什么?你想用DFS?复杂度可能是O(4*120!),也就大概是的复杂度。
所以,这道题很明显就是一道概率DP的题~
DP数组表示?
对于本题,不妨设 f[i][j][k][v] 表示1、2、3、4种生物的生命值为i、j、k、v的概率。
初始值?
很显然,对于读入的 a[1...n],可以有 f[a[1]][a[2]][a[3]][a[4]]=1 (没有读入的生命值为0)
怎么转移?
对于a[1]+a[2]+a[3]+a[4]<=m的情况,直接输出n个0.000000即可。(难道这还用问为什么?)
若a[1]+a[2]+a[3]+a[4]>m,开始dp。
1 dp[a[1]][a[2]][a[3]][a[4]]=1; 2 for (register int i=a[1];i>=0;--i) 3 for (register int v=a[2];v>=0;--v) 4 for (register int k=a[3];k>=0;--k) 5 for (register int j=a[4];j>=0;--j) 6 { 7 if(a[1]-i+a[2]-v+a[3]-k+a[4]-j>m) continue; 8 register int cnt=0; 9 if(i)++cnt; if(v)++cnt; if(k)++cnt; if(j)++cnt; 10 if(!cnt) continue; 11 double gl=1.000000/(double)cnt; 12 if(i) dp[i-1][v][k][j]+=gl*dp[i][v][k][j]; 13 if(v) dp[i][v-1][k][j]+=gl*dp[i][v][k][j]; 14 if(k) dp[i][v][k-1][j]+=gl*dp[i][v][k][j]; 15 if(j) dp[i][v][k][j-1]+=gl*dp[i][v][k][j]; 16 }
方程的含义是显而易见的。由当前状态推向下一状态,并乘以对应的概率。
如何求得答案?
举个栗子,对于一个对第一个生物生存的概率询问,我们遍历求和,sum=Σf[0][j][k][v],其中j<=a[2],k<=a[3],v<=a[4],且 a[1]+a[2]-j+a[3]-k+a[4]-v==m
这样,我们求到的sum是第一个生物死亡的概率,我们回答 1.0-sum 即可。
附上AC代码
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 template<class _T>inline void read(_T &_a) 5 { 6 char _ch=getchar();_a=0; 7 while(_ch<\'0\'||_ch>\'9\')_ch=getchar(); 8 while(_ch>=\'0\'&&_ch<=\'9\'){_a=(_a<<3)+(_a<<1)+_ch-\'0\';_ch=getchar();} 9 } 10 11 const int maxn=5; 12 int n,m,a[maxn]; 13 double dp[51][51][51][51]; 14 15 int main() 16 { 17 freopen("volcano.in","r",stdin); 18 freopen("volcano.out","w",stdout); 19 read(n); read(m); 20 for (register int i=1;i<=n;++i) read(a[i]); 21 if(m>=a[1]+a[2]+a[3]+a[4]) 22 { 23 for (register int i=1;i<=n;++i) printf("0.000000\\n"); 24 return 0; 25 } 26 dp[a[1]][a[2]][a[3]][a[4]]=1; 27 for (register int i=a[1];i>=0;--i) 28 for (register int v=a[2];v>=0;--v) 29 for (register int k=a[3];k>=0;--k) 30 for (register int j=a[4];j>=0;--j) 31 { 32 if(a[1]-i+a[2]-v+a[3]-k+a[4]-j>m) continue; 33 register int cnt=0; 34 if(i)++cnt; if(v)++cnt; if(k)++cnt; if(j)++cnt; 35 if(!cnt) continue; 36 double gl=1.000000/(double)cnt; 37 if(i) dp[i-1][v][k][j]+=gl*dp[i][v][k][j]; 38 if(v) dp[i][v-1][k][j]+=gl*dp[i][v][k][j]; 39 if(k) dp[i][v][k-1][j]+=gl*dp[i][v][k][j]; 40 if(j) dp[i][v][k][j-1]+=gl*dp[i][v][k][j]; 41 } 42 for (register int i=1;i<=n;i++) 43 { 44 register double ans=0; 45 if (i==1) 46 for (register int j=0;j<=a[2];++j) 47 for (register int k=0;k<=a[3];++k) 48 for (register int p=0;p<=a[4];++p) 49 if (a[1]+a[2]-j+a[3]-k+a[4]-p==m) 50 ans+=dp[0][j][k][p]; 51 if (i==2) 52 for (register int j=0;j<=a[1];++j) 53 for (register int k=0;k<=a[3];++k) 54 for (register int p=0;p<=a[4];++p) 55 if (a[1]-j+a[2]+a[3]-k+a[4]-p==m) 56 ans+=dp[j][0][k][p]; 57 if (i==3) 58 for (register int j=0;j<=a[1];++j) 59 for (register int k=0;k<=a[2];++k) 60 for (register int p=0;p<=a[4];++p) 61 if (a[1]-j+a[2]-k+a[3]+a[4]-p==m) 62 ans+=dp[j][k][0][p]; 63 if (i==4) 64 for (register int j=0;j<=a[1];++j) 65 for (register int k=0;k<=a[2];++k) 66 for (register int p=0;p<=a[3];++p) 67 if (a[1]-j+a[2]-k+a[3]-p+a[4]==m) 68 ans+=dp[j][k][p][0]; 69 printf("%lf\\n",1.000000-ans); 70 } 71 return 0; 72 }
以上是关于火山喷发 计蒜客16862 NOIP模拟赛 概率DP的主要内容,如果未能解决你的问题,请参考以下文章