【CodeForces 261B】Maxim and Restaurant(DP,期望)

时间:2021-03-30 19:05:46

题目链接

第一种解法是$O(n^3*p)$的:
f[i][j][k]表示前i个人进j个人长度为k有几种方案(排列固定为123..n时)。
$f[i][j][k]=f[i-1][j][k]+f[i-1][j-1][k-a[i]]$
最外层枚举t表示被卡的那个人。i=t时不加上f[i-1][j-1][k-a[i]]。
$ans={{(\sum f[n][j][k]*j*j!*(n-1-j)!)+(\sum f[n][n][k]*n)}}/(n!)$。


可以看看这篇题解

#include<cstdio>
#include<cstring>
#define N 55
int n,p,a[N];
double f[N][N][N],ans,c[N]= {1};
int main(){
    scanf("%d",&n);
    for(int i=1; i<=n; i++){
        scanf("%d",&a[i]);
        c[i]=c[i-1]*i;
    }
    scanf("%d",&p);
    for(int t=0; t<=n; t++){
        memset(f,0,sizeof f);
        f[0][0][0]=1;
        for(int i=1; i<=n; i++)
            for(int j=0; j<=i; j++)
                for(int k=0; k<=p; k++){
                    f[i][j][k]=f[i-1][j][k];
                    if(j>=1&&t!=i&&k>=a[i])f[i][j][k]+=f[i-1][j-1][k-a[i]];
                }
        for(int j=1; j<n; j++)
            for(int k=1; k<=p; k++)if(a[t]+k>p)
                ans+=c[j]*c[n-1-j]/c[n]*f[n][j][k]*j;
        for(int k=1; k<=p; k++)ans+=f[n][n][k]*n;
    }
    printf("%lf",ans);
}

 

 

 

还可以更高效,$O(n^2*p)$
参考题解

f[i][j][k]表示前i个人至少进j个人这j个人的长度和为k有几种方案(排列为123..n时)。
那么$ans=(\sum f[n][j][k]*j!*(n-j)!/(n!)$。

我原来不太理解为什么至少j个人就是这么推,问了下队友,说是因为没有卡后面的,所以有可能可以进更多人。
其实第一种方法算的f也是至少j个人,然后在后面累加ans时再卡住第j+1个人。

并且可以用滚动数组优化为2维数组。

#include<cstdio>
#define N 51
int n,a[N],p;
double fac[N]= {1},f[N][N],tol,ans;
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",a+i);
        fac[i]=fac[i-1]*i;
        tol+=a[i];
    }
    scanf("%d",&p);
    if(tol<=p)
    {
        printf("%d",n);
        return 0;
    }
    f[0][0]=1;
    for(int i=1; i<=n; i++)
        for(int j=n-1; j>=0; j--)
            for(int k=0; k+a[i]<=p; k++)
                f[j+1][k+a[i]]+=f[j][k];
    for(int j=1; j<=n; j++)
        for(int k=0; k<=p; k++)
            ans+=f[j][k]*fac[j]*fac[n-j];
    ans/=fac[n];
    printf("%lf",ans);
}