https://codeforces.com/gym/102994/problem/G
学习自https://blog.youkuaiyun.com/Irving0323/article/details/115291600
赛中想的是前i张牌选的牌之和为j的dp,然后枚举下一张牌在哪个位置插入,然而怎么转移都发现会有重复的,或者其他问题。。。
题解告诉我们设dp[i][j][k]为考虑前i张牌选了j张牌总和为k的概率,那么概率肯定就是 ( j! ) / (n*(n-1)*...(n-j+1)),也就是前j张牌是任意排列的,然后因为前j个位置第一个位置是n种选择,第j个位置就有(n-j+1)种选择,所以把他们乘起来,其实本质就是选j张的总方案数有A(n,j), 选择出这j张的概率是 1/A(n,j)
然后在计算答案的时候我们枚举哪一张牌是作为最后一次选中,让总和恰好在(a,b]之间的,把这些加进答案就行了。
那么就需要算出tmp[j][k]为考虑了所有n张牌后,选了j张,总和是k,但是这j张一定不包含第i张的概率,这是个经典套路,“消失的背包” 叉姐很多年前出过的
#include<bits/stdc++.h>
using namespace std;
const int maxl=510;
int n,a,b;double ans;
int x[maxl];
double dp[2][maxl][maxl];
double tmp[maxl][maxl];
inline void prework()
{
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++)
scanf("%d",&x[i]);
}
inline void mainwork()
{
dp[0][0][0]=1.0;
int c=0;
for(int i=1;i<=n;i++)
{
c^=1;
for(int j=0;j<=i;j++)
for(int k=0;k<=b;k++)
{
dp[c][j][k]=dp[c^1][j][k];
if(j>=1 && k>=x[i])
dp[c][j][k]+=dp[c^1][j-1][k-x[i]]*j/(n-j+1);
}
}
ans=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<n;j++)
for(int k=0;k<=b;k++)
{
tmp[j][k]=dp[c][j][k];
if(j>=1 && k>=x[i])
tmp[j][k]-=tmp[j-1][k-x[i]]*j/(n+1-j);
}
for(int j=0;j<n;j++)
for(int k=0;k<=b;k++)
if(k>a-x[i] && k<=b-x[i] && k<=a)
ans+=tmp[j][k]/(n-j);
}
}
inline void print()
{
printf("%.8f\n",ans);
}
int main()
{
prework();
mainwork();
print();
return 0;
}