zoukankan      html  css  js  c++  java
  • [HNOI2015] 亚瑟王

    Description

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

    Solution

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

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

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

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

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

    转移枚举第(i)张用没用(f[i][j]=f[i-1][j] imes (1-p[i])^{m-j}+f[i-1][j-1] imes (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
    ",ans);
        } return 0;
    }
    
  • 相关阅读:
    Java学习笔记day01
    对有序数组进行二分查找(折半查找)
    对数组进行冒泡排序
    LeetCode #344. Reverse String
    LeetCode #292. Nim Game
    LeetCode #258. Add Digits
    Android DiskLruCache完全解析,硬盘缓存的最佳方案
    Android源码解析——LruCache
    Messenger与AIDL的异同
    Android应用层View绘制流程与源码分析
  • 原文地址:https://www.cnblogs.com/YoungNeal/p/9852386.html
Copyright © 2011-2022 走看看