【BZOJ4008】[HNOI2015] 亚瑟王(DP)

点此看题面

大致题意:\(n\)张卡牌,每张卡牌有一个概率\(p_i\)和伤害值\(d_i\)。有\(m\)轮游戏,每轮游戏按序枚举每一张未被使用过的卡牌,有\(p_i\)的概率使用该卡牌,造成\(d_i\)点伤害并立刻结束该轮游戏。求造成伤害的期望值。

前言

\(Jan\ 27th\)刷题计划(3/6),算法标签:DP、概率论。

今天由于是突然接到的刷题任务通知,加上大部分时间用在做作业上,恐怕是无法完成剩余任务了。

很抱歉,今天的刷题就到此为止吧,明天开始一定每天都要完成当天的任务!

转化

首先,这题如果直接搞是比较麻烦的,需要先转化。

考虑求总伤害的期望值,其实相当于求每张卡牌造成伤害期望值的总和(显然吧)。

而单张卡牌造成伤害的期望值,就是其被使用的概率乘上它的伤害值\(d_i\)

于是问题就被转化成了,如何求单张卡牌被使用的概率。

考虑对于第\(i\)张牌,如果我们已知在前\(i-1\)张牌中使用了\(j\)张牌,那么相当于有\(m-j\)轮这张牌会被枚举到(这里的\(m-j\)轮不考虑被使用之后就不会再被枚举到),也就是这张牌会被考虑\(m-j\)次。

则它不被使用的概率就是\((1-p_i)^{m-j}\),被使用的概率就是\(1-(1-p_i)^{m-j}\)

于是问题又被转化成了,如何求前\(i\)张牌(因为\(i\)是泛指,所以此处\(i\)\(i-1\)没有差异)中使用了\(j\)张牌的概率。

动态规划

我们设\(f_{i,j}\)表示前\(i\)张牌中使用了\(j\)张牌的概率。

显然转移有两种情况:第\(j\)张牌被选择了或者没有被选择。

然后我们可以发现,转移要乘上的系数其实和上面的情况很类似,因此直接给出转移方程:

\[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}) \]

最后,按照上面的思路,我们就可以计算答案了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 220
#define M 132
#define DB double
using namespace std;
int n,m,d[N+5];DB p[N+5],t[N+5][M+5],f[N+5][N+5];
int main()
{
	RI Tt,i,j;DB ans;scanf("%d",&Tt);W(Tt--)
	{
		for(scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%lf%d",p+i,d+i);
		for(i=1;i<=n;++i) for(t[i][0]=1,j=1;j<=m;++j) t[i][j]=t[i][j-1]*(1-p[i]);//预处理幂
		for(f[0][0]=1,i=1;i<=n;++i) for(j=0;j<=i;++j)//动态规划
			f[i][j]=f[i-1][j]*t[i][m-j]+(j?f[i-1][j-1]*(1-t[i][m-j+1]):0);
		for(ans=0,i=1;i<=n;++i) for(j=0;j^i;++j) ans+=d[i]*f[i-1][j]*(1-t[i][m-j]);//统计答案
		printf("%.10lf\n",ans);
	}return 0;
}
posted @ 2020-01-27 21:25  TheLostWeak  阅读(...)  评论(...编辑  收藏