●BZOJ 4008 [HNOI2015]亚瑟王

本文解析了一道关于概率动态规划的问题,通过定义特定的状态转移方程,巧妙地计算出每种牌被取到的概率,进而得到期望造成的伤害。文章详细解释了状态转移的过程,并附上了完整的代码实现。

题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=4008
题解:

概率dp,神仙题
如果我们可以求出每种牌被取到的概率f,那么最后期望造成的伤害也就很好计算了。
定义dp[i][j]表示有j轮游戏在1~i中的某张牌处就结束的概率。
那么此时我们考虑dp[i][j]会怎样对f[i+1]造成贡献:
只剩下了R-j轮游戏进行到了第i+1张牌,怎么计算这种情况下第i+1张牌发动技能的概率g呢?
(令p为其发动技能的概率,并给这R-j轮游戏重新依次编号为1,2,……,R-j)
显然有:g=p+(1-p)*p+(1-p)^2*p+p……+(1-p)^(R-j-1)*p
上式表示重新编号后的在第1轮发动技能的概率+在第2轮发动技能的概率+……+在第R-j轮发动技能的概率。
然而不需要这么麻烦的计算,因为上面的g=1-(1-p)^(R-j),(自己YY为什么是对的吧)
然后把对f[i+1]进行贡献:f[i+1]+=dp[i][j]*g

接下来考虑如何转移dp[i][j]:
1.这R-j轮可以进行到第i+1张牌的机会都没有让其发动技能:
dp[i+1][j]+=dp[i][j]*(1-p)^(R-j)
2.这R-j轮可以进行到第i+1张牌的机会让其发动了一次技能:
dp[i+1][j+1]+=dp[i][j]*(1-(1-p)^(R-j))

然后就是不断转移dp的同时去求出f[]数组。

(真的是神仙题,题解都看了好久,好像第一次遇到这种定义了一个莫名其妙的dp状态去辅助求出另外一个东西从而得出答案的题。。。)


代码:

 

#include<bits/stdc++.h>
using namespace std;
double dp[250][150],p[250],f[250],ans;
int d[250];
int N,R,Case;
double fastpow(double a,int b){
	double ret=1;
	for(;b;a=a*a,b>>=1)
		if(b&1) ret*=a;
	return ret;
}
int main(){
	for(scanf("%d",&Case);Case;Case--){
		scanf("%d%d",&N,&R);
		for(int i=1;i<=N;i++) scanf("%lf%d",&p[i],&d[i]),f[i]=0;
		for(int i=0;i<=N;i++) for(int j=0;j<=R;j++) dp[i][j]=0;
		dp[0][0]=1; ans=0;
		for(int i=0;i<N;i++)
			for(int j=0;j<=R;j++){
				double k=fastpow(1-p[i+1],R-j);
				dp[i+1][j]+=dp[i][j]*k;
				if(j+1<=R){
					dp[i+1][j+1]+=dp[i][j]*(1-k);
					f[i+1]+=dp[i][j]*(1-k);
				}
			}
		for(int i=1;i<=N;i++) ans+=f[i]*d[i];
		printf("%.10lf\n",ans);
	}
	return 0;
}

 

  

 

转载于:https://www.cnblogs.com/zj75211/p/8541968.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值