首先要知道一个公式就是Deep Monkey得到的分数小于等于K的概率是在二项分布中能得到0~K-1这些分数的方法数的和除以2^N。
设可能达到的最大分数是tot
所以本题的关键就是求出0到tot这些分数有多少种得到的方案数(比如样例1 2 3 ,得到3就有3和1+2两种方案)
dp【i】【j】表示前i个数能得到j分的方案数。
那么考虑第i+1个数时,得到j既有可能是前i个数构成的,也可能是前i个数构成了j-a[i+1],再加上第i+1个数构成j。那么转移方程就出来了,就是:
dp[i][j]=dp[i-1][j];
if(j>=a[i]) dp[i][j]+=dp[i-1][j-a[i]];
初始化时要把dp[0~N][0]都处理成1,这样处理到第i个数时才会把dp[i][a[i]]+1。
最后加和的时候要注意从dp[N][0]开始加,因为1分不得也是一种情况,wa了一次。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
#define eps 1e-9
LL dp[50][40005];
int a[50];
int N;
double p;
LL C[50];
void init()
{
C[1]=2;
for(int i=2;i<=41;i++)
{
C[i]=C[i-1]*2;
}
}
int main()
{
int T;
init();
cin>>T;
while(T--)
{
scanf("%d%lf",&N,&p);
int tot=0;
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
tot+=a[i];
}
memset(dp,0,sizeof(dp));
for(int i=0;i<=N;i++)
{
dp[i][0]=1;
}
for(int i=1;i<=N;i++)
{
for(int j=1;j<=tot;j++)
{
dp[i][j]=dp[i-1][j];
if(j>=a[i]) dp[i][j]+=dp[i-1][j-a[i]];
}
}
LL cur=0;
for(int i=0;i<=tot;i++)
{
cur+=dp[N][i];
double tmp=cur*1.0/C[N];
if(tmp+eps>p){
printf("%d\n",i);
break;
}
}
}
return 0;
}