BZOJ4008. [HNOI2015]亚瑟王 期望概率dp

看到这道题想什么? 一个好转移的状态由于T最多444所以把每个点控制在O(400000)以内,所以对于n和r最多乘一次因此猜f[n][r],f[r][n],首先一轮一轮的搞不好转移,那么先想一想f[n][r],如果是从头开始,在转移到下一位的时候,前面的会对后面的有恶心的影响,那么倒着来f[i][j]=(1.0-p[i])j*f[i+1][j]+[1.0-(1.0-p[i])j*(f[i+1][j-1]+d[i]),

现在让我们分析一下呢我们用到了小数点后上百位而没有被卡精的秘诀(巧合),让我们分析状态的递进首先在最后一位,因为double 16 所以点前3点后13,那么由于乘法是和小数乘因此我们的精度不会有影响(最高位往后数17位正好是我们原本的限度),那么我们分析加法,当我们进位的时候当时不会有影响,其他时候会有影响,因为我们可以把每一个位置的所有状态视为同一精度,那么加法是两个同位相加,会影响往前一位,两位,甚至是3位,4位......就是从限度开始少进几,几十,甚至几百位,但是考虑随机数据,我们被卡8位当且仅当你是一个在逃罪犯...

#include<cstdio>
#include<cstring>
#include<iostream>
#define N 222
#define M 140
using namespace std;
typedef double D;
D p[N],t[N][M],f[N][M];
int n,r,d[N];
void Init()
{ 
   scanf("%d%d",&n,&r);
   for(int i=1;i<=n;i++)
   {
     scanf("%lf",&p[i]);
     scanf("%d",&d[i]);
     t[i][1]=1.0-p[i];
     for(int j=2;j<=r;j++)
      t[i][j]=t[i][j-1]*(1.0-p[i]);
   }
   for(int i=1;i<=r;i++)
     f[n][i]=(1.0-t[n][i])*d[n];
}
void work()
{
   for(int i=n-1;i>0;i--)
     for(int j=1;j<=r;j++)
      f[i][j]=t[i][j]*f[i+1][j]+(1.0-t[i][j])*(f[i+1][j-1]+d[i]);
   printf("%.10lf\n",f[1][r]);
}
int main()
{
   freopen("arthur.in","r",stdin);
   freopen("arthur.out","w",stdout);
   int T;
   scanf("%d",&T);
   while(T--)
   {
     Init();
     work();
   }
   return 0;
}

 

posted @ 2017-07-05 14:36  TS_Hugh  阅读(179)  评论(0编辑  收藏  举报