[51nod 1450] 闯关游戏

一个游戏App由N个关卡组成,每个关卡有不同概率获得不同星星。玩家需达到所有关卡成功且总星星数至少为M才能通关。通过分析每个关卡的成功和两颗星期望,按照Y值排序进行期望最小化计算,得出通关的期望时间E。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

一个游戏App由N个小游戏(关卡)构成,将其标记为0,1,2,..N-1。这些小游戏没有相互制约的性质,玩家可以任意时刻玩任意一个小游戏,且每个小游戏可以玩任意多次,一个小游戏玩一次消耗玩家恰好1min的时间。每个小游戏会根据玩家的表现返回3种结果:1)挑战失败;2)挑战成功并获得1颗星;3)挑战成功且获得2颗星。玩家可以多次挑战同一个小游戏,而且系统会记录玩家多次挑战中的最好成绩。(注意:两颗星优于一颗星优于挑战失败。)
这个游戏App通关需要同时满足2个条件:1)N个小游戏的系统记录的最好成绩都是成功;2)这N个小游戏的系统记录成绩中的星星总数至少是M颗。
根据一些统计,一个玩家在任一次玩第i个小游戏时,都会独立的发生以下结果:
* 有(1000 - X[i] - Y[i])*0.001的概率会挑战失败;
* 有 X[i]*0.001 的概率会挑战成功并获得一颗星;
* 有 Y[i]*0.001 的概率会挑战成功并获得两颗星.
其中1<=X[i],Y[i],且X[i]+Y[i]<=1000,且都为整数.
问一个玩家从安装完这个游戏App到这个游戏App通关,最优策略下需要花费时间的期望E。输出E的值,以min为该时间单位。(误差在1e-7内)

例如:样例中有两个小游戏,且两个小游戏每次玩必能通过,且有0.5的概率拿两颗星。玩家可以先各玩一个小游戏一次,有0.75的概率能拿至少3颗星;有0.25的概率只能拿2颗星,此时需要盯着一关不停的玩,直到得到两颗星,这需要1/2 * 1 + 1/4 * 2 + 1/8 * 3 + … + 1/(2^k) * k + …. 次。所以,E = 0.75*2 + 0.25*(2 + 1/2 * 1 + 1/4 * 2 + 1/8 * 3 + … + 1/(2^k) * k + …. )= 2.5。

分析

我们可以先搞出每个游戏挑战成功和一定获得两颗星的期望,因为必定有一些点要搞两颗星,每个游戏都必须要成功。
额…好久没打期望题,复习一下解方程…设两颗星期望为x, x=1Y[i]1000+x(1Y[i]1000) ,那么 x=1000Y[i]
现在考虑怎么求总的期望。
你肯定不能说有一些一定两颗星,一些一定一颗星来分配方案,因为有时候你想要分配一颗星的地方可能爆出两颗星,那么期望肯定大了。
我们发现当要求一些点只求成功的时候,一颗星或者两颗星的概率是固定的,那么我们如果按照一定的顺序只求成功,到最后就要全部取两颗星了。哎,考虑让两颗星的期望最小,我们发现按照Y排序,这样的顺序,取两颗星的期望和最小。那么前面的部分,考虑记录一些状态来储存所有局面,设f[i][j]表示玩了前i个游戏,获得j颗星的期望(已经乘上概率,最后统计答案直接加上即可),g[i][j]表示到达这个局面的概率。那么你就可以递推了。在 mj=(ni)2 的时候,后面就要全部取两颗星了,不用再转移了。只求成功的时候,一颗星和两颗星的概率比例是 X[i]:Y[i] 的,这样就解决了。

#

 #include<cstdio> 
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmin(a,b) (a=(a<b)?a:b)
#define cmax(a,b) (a=(a>b)?a:b)
const int N=4000+5;
db s1[N],s2[N],p1[N],p2[N],ans; 
db f[2][N],g[2][N];
int X[N],Y[N],n,m,i,j,d[N],q1,q2;
bool cmp(int x,int y)
{
    return Y[x]<Y[y];
}
int main()
{
    freopen("t1.in","r",stdin);
    //freopen("t1.out","w",stdout);
    scanf("%d %d",&n,&m);
    fo(i,1,n) scanf("%d %d",&X[i],&Y[i]),d[i]=i;
    sort(d+1,d+1+n,cmp);
    fo(j,1,n)
    {
        i=d[j];
        s1[j]=1000.0/db(X[i]+Y[i]);
        s2[j]=1000.0/db(Y[i]);
        p1[j]=db(X[i])/db(X[i]+Y[i]);;
        p2[j]=db(Y[i])/db(X[i]+Y[i]);;
    }
    fd(i,n-1,1) s2[i]+=s2[i+1];
    ans=0;
    f[0][0]=0;
    g[0][0]=1.0;
    q1=0;
    q2=1;
    fo(i,0,n-1)
    {
        fo(j,0,i*2)
        if (g[q1][j]!=0.0)
        {
            if ((n-i)*2==m-j)
                ans+=g[q1][j]*s2[i+1]+f[q1][j];
            else
            {
                f[q2][j+1]+=(f[q1][j]+s1[i+1]*g[q1][j])*p1[i+1];
                g[q2][j+1]+=g[q1][j]*p1[i+1];
                f[q2][j+2]+=(f[q1][j]+s1[i+1]*g[q1][j])*p2[i+1];
                g[q2][j+2]+=g[q1][j]*p2[i+1];
            }
        }
        fo(j,0,i*2) f[q1][j]=g[q1][j]=0;
        q1^=1;
        q2^=1;
    }
    fo(j,m,i*2) 
        ans+=f[q1][j];
    printf("%.7lf",ans);
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值