【BZOJ 4008】【HNOI 2015】[概率DP]亞瑟王

題目描述

直接給鏈…太懶了(傳送門)

題目解析

這題太毒瘤了…尼瑪還可以轉換…
首先,直接dp貌似是不行的(反正我這個LaJi不行),那麼,我們迴歸本質。
ans=Σni(Pi×Wi)
Wi 就是給出的傷害值,那麼Pi 怎麼求呢?
神奇的來了,我們不妨設dpi,j 表示第i 張卡片被嘗試發動了恰好j 次。
那麼Pi=Σnj(dpi,j×(1(1pi)j)) ,因爲如果嘗試發動j 次那麼只會有(1pi)j 的機率每次失敗,那麼至少有一次成功的概率就是(1(1pi)j) ,拿它乘以原概率就行了。
那麼dpi,j 怎麼求呢?我們可以知道若第i1 張卡牌有j 次被嘗試,如果都失敗了,那麼第i 張卡牌就一定有j 次機會。若有至少有一次成功那麼第i 張卡牌就一定會有j1 次機會。於是我們可以推出:dpi,j=dpi1,j×(1pi1)j+dpi1,j+1×(1(1pi)j+1)
然後ans=ΣniΣrjdpi,j×(1(1pi)j)×di
現在就有一個問題,那就是(1(1pi)j) 本來是發動至少一次的概率,而我們需要的是剛好發動一次的概率,其實在這道題中因爲每張牌只能發動一次,他倆是等效的,你可以想,假設有一種情況有k個被選擇發動,事實只有第一個纔會發動,於是一定存在另一種情況使得k箇中有一個不被選擇,兩者的概率之和便是有k-1個被選擇發動的概率,如果繼續下去便會到只有一個被選擇發動。
只發動一次的概率爲:Σj+1k=1(1pi)k1×pi×1×1...=pi×(1pi)j+11(1pi)1=1(1pi)j+1

代碼

#include<cstdio>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cmath>
using namespace std;

#define MAXN 220
#define MAXR 132
#define INF 0x3f3f3f3f
typedef long long int LL;

template<class T>
void Read(T &x){
    x=0;char c=getchar();bool flag=0;
    while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
    while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
    if(flag)x=-x;
}

int n,r;
long double p[MAXN+10],w[MAXN+10];
long double dp[MAXN+10][MAXR+10];

int main(){
    int T;
    Read(T);
    while(T--){
        memset(dp,0,sizeof(dp));

        Read(n),Read(r);
        for(int i=1;i<=n;++i)cin>>p[i]>>w[i];

        dp[0][r]=1;
        long double ans=0;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=r;++j){
                dp[i][j]=dp[i-1][j]*pow(1-p[i-1],j)+dp[i-1][j+1]*(1-pow(1-p[i-1],j+1));
                ans+=dp[i][j]*(1-pow(1-p[i],j))*w[i];
            }

        cout<<fixed<<setprecision(10)<<ans<<'\n';
    }
}

/*
2

3 2
0.5000 2
0.3000 3
0.9000 1

3 2
0.5000 2
0.3000 3
0.9000 1
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章