bzoj4008亚瑟王

2天前写的,现在才写题解= =,这题主要是有个问题无法解决,之前发动过的卡牌这次会直接跳过。这样如果要把第几轮作为dp数组的一维的话,就需要用状态压缩存储之前的情况。所以需要另外一种状态设计方法。我们发现如果一轮要访问到第i张牌,那么1-i-1一定也访问过一遍,这就是这题最巧妙的地方。

f[i][j]为第i张牌被访问j次的概率,j包括被触发和被跳过的次数。

初始条件f[1][r]=1.0,因为每次都从第一张开始

f[i][j]+=f[i-1][j]*(1-p[i-1])^j; 第i-1张牌还没有发动,所以跟第i张牌都是j次

f[i][j]+=f[i-1][j+1]*(1-(1.0-p[i-1])^(j+1)) 代表i-1至少在j+1次访问中发动了一次,所以才会第i张牌对第i-1张牌少一次

最后答案就是算出每张牌发动的概率,对于每个f[i][j],他对第i张牌发动的概率就是f[i][j]*(1-(1-p[i])^j),代表j次访问中至少发动过一次。

#include<bits/stdc++.h>
#define maxl 410
using namespace std;

int n,r;
double d[maxl],p[maxl],q[maxl];
double qq[maxl][maxl],f[maxl][maxl];
double ans[maxl];

inline void prework()
{
  scanf("%d%d",&n,&r);
  for(int i=1;i<=n;i++)
    {
      scanf("%lf%lf",&p[i],&d[i]);
      q[i]=1.0-p[i];
    }
  for(int i=1;i<=n;i++)
    {
      qq[i][0]=1.0;
      for(int j=1;j<=r;j++)
	qq[i][j]=qq[i][j-1]*q[i];
    }
  for(int j=1;j<=r;j++)
    qq[0][j]=1.0;
}

inline void mainwork()
{
  memset(f,0,sizeof(f));
  memset(ans,0,sizeof(ans));
  /*f[1][0]=0;
  for(int j=1;j<=r;j++)
    f[1][j]=1;*/
  f[1][r]=1.0;
  for(int i=2;i<=n;i++)
    for(int j=0;j<=r;j++)
      f[i][j]=f[i-1][j]*qq[i-1][j]+f[i-1][j+1]*(1-qq[i-1][j+1]);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=r;j++)
      ans[i]+=f[i][j]*(1-qq[i][j]);
}

inline void print()
{
  double sum=0;
  for(int i=1;i<=n;i++)
    sum+=d[i]*ans[i];
  printf("%.10f\n",sum);
}

int main()
{
  int t;
  scanf("%d",&t);
  for(int i=1;i<=t;i++)
    {
      prework();
      mainwork();
      print();     
    }
  return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值