P3239 [HNOI2015]亚瑟王
发一个不一样的做法。
我目前没有找到一样的解法,大家都是概率期望两步走,而此做法并不是。
做法来源于我的一个朋友:_Veritas
我让他发个题解,他虽然答应了,但是由于已经开学,可能会鸽一会,所以我先发一篇,等他发了推荐大家去看原创。
原创链接:暂无
题意简述:
有
分析:
一个比较不好处理的条件就是如果触发一次攻击,马上会重新开始一轮。
考虑转化问题,原问题可以转化为:一次性地从第
如此,问题就转化成了序列上的概率期望dp问题,设
状态转移:
解释一下:考虑到第
如果把状态转移看成一个有向无环图的话,上文中的“其指向的情况”其实就是一条有向边,如果知道绿豆蛙的归宿为什么要反向建边,就同样可以理解为什么要倒推。
再多说一点,绿豆蛙的归宿也可以不反向建边,那就要概率期望两步走。而此题几乎所有人的做法都是正着推,其实就是概率期望两步走,类似于绿豆蛙的归宿那道题正着建图的做法。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int T,n,r;
double p[222],d[222],dp[222][150];
double Pow(double a, int m)
{
double base=a, res=1;
while(m>0)
{
if(m&1) res*=base;
base*=base, m>>=1;
}
return res;
}
int main()
{
cin>>T;
while(T--)
{
memset(dp,0,sizeof(dp));
cin>>n>>r;
for(int i=1;i<=n;++i) cin>>p[i]>>d[i];
for(int i=n;i>0;--i)
{
for(int j=1;j<=r;++j)
{
double P=Pow(1.0-p[i],j);
dp[i][j]=dp[i+1][j]*P+(dp[i+1][j-1]+d[i])*(1.0-P);
}
}
printf("%.10lf\n", dp[1][r]);
}
return 0;
}