题意:
- 给定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; }