bzoj2676 Contra

题意:

  • 给定N,R,Q,S
  • 有N个关卡,初始有Q条命,且任意时刻最多只能有Q条命
  • 每通过一个关卡,会得到u分和1条命,其中u=min(最近一次连续通过的关数,R)
  • 若没有通过这个关卡,将失去一条命,并进入下一个关卡
  • 若没有生命或N个关卡均已挑战过一次时,游戏结束,得到的分数为每关得到的分数的总和
  • 每条命通过每个关卡的概率为p(0<=p<=1),原先最高分纪录为S
  • 求当p至少为多少时,期望获得的总分能够超过最高分。
  • 1<=N<=10^8 1<=R<=20 1<=Q<=5,输出保留6位小数

李超WC2013课件里的题(题意就是从课件上粘下来的).

定义f[i][j][k]为当前有i条命,下一次胜利的得分为j(j=min(当前连胜场次+1,R)),还有k个关卡时的期望得分

那么f[i][j][k]=p*(f[min(i+1,Q)][min(j+1,R)][k-1]+j)+(1-p)*f[i-1][1][k-1],边界f[0][j][k]=0

把[i][j]两维展开成一维,就可以通过矩阵乘法来转移了.看起来矩阵会很大会超时,不过我们可以精细地实现程序,因为构造出的转移矩阵里大部分数字是0,我们只需要避免和0相乘就可以过了.(李超在课件里说可以减少无用的状态:连胜次数大于Q的时候生命值不可能小于Q)

然后我写挂了…原因是数组两维大小(5和20)开反了,把大小为5的一维当作大小为20来用,狂WA….

#include<cstdio>
#include<cstring>
int n,r,q;long long s;
int sz;
struct matrix{
  double a[121][121];
  matrix(){memset(a,0,sizeof(a));}
  matrix(int x){memset(a,0,sizeof(a));for(int i=0;i<sz;++i)a[i][i]=x;}
  matrix operator *(const matrix &B)const{
    matrix C;
    for(int i=0;i<sz;++i){
      for(int j=0;j<sz;++j){
    if(a[i][j]<1e-15)continue;
    for(int k=0;k<sz;++k){
      if(B.a[j][k]<1e-15)continue;
      C.a[i][k]+=a[i][j]*B.a[j][k];
    }
      }
    }
    return C;
  }
}A;
void QuickPow(matrix &A,int x){//printf("%d\n",x);
  matrix Ans(1);
  for(;x;x>>=1,A=A*A){//printf("...");
    if(x&1)Ans=Ans*A;
  }
  A=Ans;
}
int conv[7][22];//conv[22][7]=>conv[7][22],WA=>AC
bool check(double ans){
  sz=r*q+1;
  int tot=0;
  for(int i=1;i<=q;++i){
    for(int j=1;j<=r;++j){
      conv[i][j]=tot++;
    }
  }
  A=matrix(0);A.a[tot][tot]=1.0;
  for(int i=1;i<=q;++i){
    for(int j=1;j<=r;++j){
      A.a[tot][conv[i][j]]+=ans*j;
      if(i<q&&j<r){
    A.a[conv[i+1][j+1]][conv[i][j]]+=ans;
      }else if(i<q){
    A.a[conv[i+1][j]][conv[i][j]]+=ans;
      }else if(j<r){
    A.a[conv[i][j+1]][conv[i][j]]+=ans;
      }else{
    A.a[conv[i][j]][conv[i][j]]+=ans;
      }
      if(i>1){
    A.a[conv[i-1][1]][conv[i][j]]+=(1-ans);
      }
    }
  }
  // for(int i=0;i<sz;++i){
  //   for(int j=0;j<sz;++j){
  //     printf("%.2f ",A.a[i][j]);
  //   }printf("\n");
  // }
  //printf("here");
  QuickPow(A,n);//printf("done");
  double sum=A.a[tot][conv[q][1]];//printf("%f\n",sum);
  return sum>s;
}
int main(){
  scanf("%d%d%d%lld",&n,&r,&q,&s);
  if(!check(1.0)){
    puts("Impossible.");
  }else{//while(1);
    double l=0,r=1.0;
    while(r-l>1e-8){//printf("!");
      double mid=(l+r)/2;
      if(check(mid))r=mid;
      else l=mid;
    }
    printf("%.6f\n",(r+l)/2);
  }
  return 0;
}

 

posted @ 2017-02-15 19:39  liu_runda  阅读(517)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难