[Lydsy2017年4月月赛]抵制克苏恩题解
考试的时候以为就是简单的概率期望题,考完后知道是简单的概率期望DP题,完美爆零。
这道题数据范围很小,很容易让人想到状压,不过貌似没什么可压的。那么只能说明这道题复杂度很高了,状态数组f[o][i][j][k]为第o次攻击后出现有i个1滴血的j个2滴血的,k个3滴血的情况的概率。那么转移方程就明了了。
1.f[o][i][j][k]+=f[o-1][i][j][k]/(i+j+k+1)英雄收到攻击
2.f[o][i][j][k]+=f[o-1][i+1][j][k]*(i+1)/(i+j+k+2)有一个1滴血的奴隶主被击杀。前提是i+j+k+1<=7
3.f[o][i][j][k]+=f[o-1][i-1][j+1][k-1]*(j+1)/(i+j+k)有一个2滴血的奴隶主收到攻击,召唤一个奴隶主,前提是i!=0&&k!=0
4.f[[o][i][j][k]+=f[o-1][i][j-1][k]*(k/i+j+k)有一个3滴血的奴隶主收到攻击,召唤一个奴隶主,前提是j!=0。
5由于仆从满7个后不会再召唤,所以当i+j+k=7时需要特判受到攻击却未召唤的情况,即f[o][i][j][k]+=f[o-1][i-1][j+1][k],f[o][i][j][k]+=f[o-1][i][j-1][k+1],前提分别为i!=0,j!=0。
转移方程写出来别挂精度上就行了,但是要注意我们的状态为受到攻击后场上局面的状态,真正结果为0~k-1中所有状态转移方程中1/仆从+1,一开始写的为1~k身败名裂……
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 int t,n; 10 double f[55][10][10][10]; 11 int main(){ 12 // freopen("defcthun.in","r",stdin); 13 // freopen("defcthun.out","w",stdout); 14 scanf("%d",&t); 15 while(t--) 16 { 17 memset(f,0,sizeof(f)); 18 scanf("%d",&n); 19 int xx,yy,zz; 20 scanf("%d%d%d",&xx,&yy,&zz); 21 f[0][xx][yy][zz]=1.0; 22 for(int o=1;o<n;o++) 23 { 24 for(int i=0;i<=7;i++) 25 { 26 for(int j=0;j<=7;j++) 27 { 28 for(int k=0;k<=7;k++) 29 { 30 if(i+j+k>7)break; 31 if(i+j+k+1<=7) f[o][i][j][k]+=f[o-1][i+1][j][k]*(double(i+1)/double(i+1+j+k+1)); 32 f[o][i][j][k]+=f[o-1][i][j][k]/double(i+j+k+1); 33 if(i&&k) f[o][i][j][k]+=f[o-1][i-1][j+1][k-1]*(double(j+1)/double(i+k+j)); 34 if(j) f[o][i][j][k]+=f[o-1][i][j-1][k]*(double(k)/double(i+j+k)); 35 if(i+j+k==7) 36 { 37 if(i) f[o][i][j][k]+=f[o-1][i-1][j+1][k]*double(j+1)/double(i+j+k+1); 38 if(j) f[o][i][j][k]+=f[o-1][i][j-1][k+1]*double(k+1)/double(i+j+k+1); 39 } 40 } 41 } 42 } 43 } 44 double sum=0.0; 45 for(int o=0;o<n;o++) 46 { 47 for(int i=0;i<=7;i++) 48 { 49 for(int j=0;j<=7;j++) 50 { 51 for(int k=0;k<=7;k++) 52 { 53 if(i+j+k>7)break; sum+=f[o][i][j][k]*1.0/double(j+k+i+1); 54 } 55 } 56 } 57 } 58 printf("%.2lf\n",sum); 59 } 60 //while(1); 61 return 0; 62 }