[HNOI2015] 亚瑟王

Description

现在有\(N(N\leq 220)\)张卡牌,每张卡牌只能被使用一次。要进行\(1\)\(M(M\leq 150)\)轮游戏,每轮游戏从\(1\)号卡牌向后扫,如果当前的卡牌在之前的游戏中使用过了,就跳过这张卡牌,否则有\(p_i\)的概率使用此卡牌。如果使用,那么本轮游戏结束,进入下一轮,否则扫下一张卡牌。扫完第\(n\)张卡牌后结束本轮游戏。每张卡牌有攻击力\(d_i\),如果被选中就会造成一定伤害,求\(1\)局游戏的期望造成伤害和。

Solution

题外话:傻库今天好牛逼!库昊回来了!

根据期望的线性性,如果能求出第\(i\)张卡牌使用的概率\(fp_i\),那么答案就是\(\sum\limits_{i=1}^n fp_i\times d_i\)

如何求这个\(fp_i\)就是关键了。

观察到这个题比较麻烦的是如果发动了一次技能那么就要立即结束此轮游戏。

可以定义一个状态\(f[i][j]\)表示前\(i\)张卡牌\(M\)轮一共用了\(j\)张的概率。

转移枚举第\(i\)张用没用\(f[i][j]=f[i-1][j]\times (1-p[i])^{m-j}+f[i-1][j-1]\times (1-(1-p[i])^{m-j})\)

这个\((1-p[i])^{m-j}\)实际上就是在\(m-j\)轮都跳过了第\(i\)张的概率,再去用\(1\)减一下就是至少用了\(1\)次的概率。可以写几个式子自己看看发现这样是对的。

考虑答案存在哪里。

对于第\(i\)张卡牌,我们可以知道前\(i-1\)张用了\(j\)张牌的概率,然后再乘上强制选中第\(i\)张的概率就是前\(i\)张用了\(j+1\)张且一定用了第\(i\)张的概率,做一个求和就行了。

Code

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cctype>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using std::min;
using std::max;
using std::swap;
using std::vector;
const int N=225;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)

int n,m;
db p[N],d[N],f[N][N];

int getint(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch))w|=ch=='-',ch=getchar();
    while( isdigit(ch))X=X*10+ch-48,ch=getchar();
    if(w) return -X;return X;
}

signed main(){
    int T=getint();
    while(T--){
        memset(f,0,sizeof f);
        n=getint(),m=getint();
        for(int i=1;i<=n;i++)
            scanf("%lf%lf",&p[i],&d[i]);
        f[0][0]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=min(m,i);j++){
                if(!j) f[i][j]=f[i-1][j]*pow(1-p[i],m-j);
                else f[i][j]=f[i-1][j]*pow(1-p[i],m-j)+f[i-1][j-1]*(1-pow(1-p[i],m-j+1));
            }
        }
        db ans=0;
        for(int i=1;i<=n;i++){
            db now=0;
            for(int j=0;j<=min(i-1,m);j++)
                if(j!=m)
                    now+=f[i-1][j]*(1-pow(1-p[i],m-j));
            ans+=now*d[i];
        } printf("%.10lf\n",ans);
    } return 0;
}
posted @ 2018-10-25 19:54  YoungNeal  阅读(177)  评论(0编辑  收藏  举报