bzoj 4008 [HNOI2015] 亚瑟王
bzoj 4008 [HNOI2015] 亚瑟王
题面
[题面][https://www.lydsy.com/JudgeOnline/problem.php?id=4008]
题解
我们令\(f[i][j]\)表示当前考虑了前\(i\)张卡牌,前\(i\)张牌一共剩下\(j\)次机会的概率是多少
那么\(f[0][r]=1\)
然后递推地求解
如果第\(i+1\)张卡牌没有用到 那么\(f[i+1][j]+=f[i][j]\times (1-p[i+1])^j\)
如果第\(i+1\)张卡牌被用到了,那么少了一次机会,多了一些伤害
\(f[i+1][j-1]+=f[i][j]\times (1-(1-p[i+1])^j)\)
然后答案会加\(f[i][j]\times(1-(1-p[i+1])^j)\times d[i+1]\)
就是概率乘伤害
Review
这题据TRCYX说是NewTrain最难得期望dp
我觉得主要是状态难想,因为考虑到某一张牌只和前面的牌有关,和后面的牌无关,而且出牌的顺序也不影响答案,于是把\(r\)轮搞成\(r\)次机会,所以想到这个状态
Code
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int T;
int n,r,d[255];
double p[255];
double f[255][255],pw[255][255];
int main()
{
T=read();
while(T--)
{
n=read();r=read();
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
{
scanf("%lf",&p[i]);
d[i]=read();
}
double ans=0;
for(int i=1;i<=n;i++)
{
pw[i][0]=1;
for(int j=1;j<=r;j++)pw[i][j]=pw[i][j-1]*(1-p[i]);
}
f[0][r]=1;
for(int i=0;i<n;i++)
for(int j=0;j<=r;j++)
{
f[i+1][j]+=f[i][j]*pw[i+1][j];
if(j-1>=0)
{
f[i+1][j-1]+=f[i][j]*(1-pw[i+1][j]);
ans+=f[i][j]*(1-pw[i+1][j])*d[i+1];
}
}
printf("%.10lf\n",ans);
}
return 0;
}