2天前写的,现在才写题解= =,这题主要是有个问题无法解决,之前发动过的卡牌这次会直接跳过。这样如果要把第几轮作为dp数组的一维的话,就需要用状态压缩存储之前的情况。所以需要另外一种状态设计方法。我们发现如果一轮要访问到第i张牌,那么1-i-1一定也访问过一遍,这就是这题最巧妙的地方。
f[i][j]为第i张牌被访问j次的概率,j包括被触发和被跳过的次数。
初始条件f[1][r]=1.0,因为每次都从第一张开始
f[i][j]+=f[i-1][j]*(1-p[i-1])^j; 第i-1张牌还没有发动,所以跟第i张牌都是j次
f[i][j]+=f[i-1][j+1]*(1-(1.0-p[i-1])^(j+1)) 代表i-1至少在j+1次访问中发动了一次,所以才会第i张牌对第i-1张牌少一次
最后答案就是算出每张牌发动的概率,对于每个f[i][j],他对第i张牌发动的概率就是f[i][j]*(1-(1-p[i])^j),代表j次访问中至少发动过一次。
#include<bits/stdc++.h>
#define maxl 410
using namespace std;
int n,r;
double d[maxl],p[maxl],q[maxl];
double qq[maxl][maxl],f[maxl][maxl];
double ans[maxl];
inline void prework()
{
scanf("%d%d",&n,&r);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&p[i],&d[i]);
q[i]=1.0-p[i];
}
for(int i=1;i<=n;i++)
{
qq[i][0]=1.0;
for(int j=1;j<=r;j++)
qq[i][j]=qq[i][j-1]*q[i];
}
for(int j=1;j<=r;j++)
qq[0][j]=1.0;
}
inline void mainwork()
{
memset(f,0,sizeof(f));
memset(ans,0,sizeof(ans));
/*f[1][0]=0;
for(int j=1;j<=r;j++)
f[1][j]=1;*/
f[1][r]=1.0;
for(int i=2;i<=n;i++)
for(int j=0;j<=r;j++)
f[i][j]=f[i-1][j]*qq[i-1][j]+f[i-1][j+1]*(1-qq[i-1][j+1]);
for(int i=1;i<=n;i++)
for(int j=1;j<=r;j++)
ans[i]+=f[i][j]*(1-qq[i][j]);
}
inline void print()
{
double sum=0;
for(int i=1;i<=n;i++)
sum+=d[i]*ans[i];
printf("%.10f\n",sum);
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}