您的位置:首页 > 其它

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

2017-03-11 10:27 337 查看

题目描述

直接给链…太懒了(传送门)

题目解析

这题太毒瘤了…尼玛还可以转换…

首先,直接dp貌似是不行的(反正我这个LaJi不行),那么,我们回归本质。

ans=Σni(Pi×Wi)

Wi就是给出的伤害值,那么Pi怎么求呢?

神奇的来了,我们不妨设dpi,j表示第i张卡片被尝试发动了恰好j次。

那么Pi=Σnj(dpi,j×(1−(1−pi)j)),因为如果尝试发动j次那么只会有(1−pi)j的几率每次失败,那么至少有一次成功的概率就是(1−(1−pi)j),拿它乘以原概率就行了。

那么dpi,j怎么求呢?我们可以知道若第i−1张卡牌有j次被尝试,如果都失败了,那么第i张卡牌就一定有j次机会。若有至少有一次成功那么第i张卡牌就一定会有j−1次机会。于是我们可以推出:dpi,j=dpi−1,j×(1−pi−1)j+dpi−1,j+1×(1−(1−pi)j+1)

然后ans=ΣniΣrjdpi,j×(1−(1−pi)j)×di

现在就有一个问题,那就是(1−(1−pi)j)本来是发动至少一次的概率,而我们需要的是刚好发动一次的概率,其实在这道题中因为每张牌只能发动一次,他俩是等效的,你可以想,假设有一种情况有k个被选择发动,事实只有第一个才会发动,于是一定存在另一种情况使得k个中有一个不被选择,两者的概率之和便是有k-1个被选择发动的概率,如果继续下去便会到只有一个被选择发动。

只发动一次的概率为:Σj+1k=1(1−pi)k−1×pi×1×1...=pi×(1−pi)j+1−1(1−pi)−1=1−(1−pi)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
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bzoj 动态规划 概率dp