列队春游

列队春游

题目描述

image

输入格式

image

输出格式

image

样例

image

CODE

1.dfs TLE 20%

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 302;
const int maxm = 9e4 + 10;

int n, h[maxn], b[maxn];
ll sum=1, ans;
bool v[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

ll check()
{
    ll ans = 0;
    /*for(int i=1; i<=n; i++)
    {
        printf("%d ", b[i]);
    }
    printf("\n");*/
    for(int i=n; i>=1; i--)
    {
        int can = i;
        while(can--)
        {
            ans++;
            if(b[i] <= b[can]) break;
            //printf("b[%d]=%d b[%d]=%d\n", i, b[i], can, b[can]);
        }
    }
    //printf("ans == %lld\n", ans);
    return ans;
}

ll dfs(int now, int n)
{
    ll ans = 0;
    if(now > n)
    {
        return check();
    }
    for(int i=1; i<=n; i++)
    {
        if(v[i]) continue;
        v[i] = 1;
        b[now] = h[i];
        ans += dfs(now+1, n);
        v[i] = 0;
    }
    return ans;
}

int main()
{
    n = read();
    for(int i=1; i<=n; i++)
    {
        h[i] = read();
    }
    for(int i=2; i<=n; i++)
    {
        sum *= i;
    }
    ans = dfs(1, n);
    printf("%.2lf", (double)ans/sum);

    return 0;
}
View Code

2.正解

假设比i矮的有s个,称为s集合,对于j属于s,设f[i]为前面期望比i矮的个数,d[j]是j对i的贡献(在个数上),p[j]是j对i有贡献的概率。

d[i] = 1 * p[j], f[i] = sigma d[j](j属于s) , p[j] = (j对i有贡献的排列数)/(n个学生的排列总数)= (n-s)!*A(n, s-1) / n! = 1 / (n-s+1)

//详细解释:摘抄自MaxMercer的博客

 我们先忽略掉除s集合里j之外的s-1个人. 此时我们算出j刚好在i前面一位的排列数. 怎么算呢? 现在删去s-1个人, 还剩n-s+1个人. 因为j在刚好前一位, 所以我们假设j和i是一个人, 那么对剩下n-s个人(j和i合并), 随意乱排列的排列数是 (n-s)!. 因为j和i我们看做一个人, 那么此时(n-s)!可以看做n-s+1个人j在i刚好前一位的所有排列数. 然后我们再把删去的s集合里的s-1个人插进这个排列里. 这样j和i之间的人就肯定是s集合里的了. 把s-1个人插回去的排列数是(n-s)! *  n!  / (n - s + 1)! , 即(n-s)! * A(n, s-1). 那么p[j] = (n-s)! * A(n, s-1) / n!. 简化而得p[j] = 1 / (n - s + 1).

//摘抄自bfk的博客

要满足j对i有贡献,那么j与i之间只能有其他属于s集合的学生,有s-1个。
先忽略这s-1个学生,现在还剩n-s+1个学生。那么这些学生的排列中,j刚好在i前一个的排列有: (n-s)! 个
而剩下(s-1)个比i矮的学生的分散在上述排列中的排列有
(n-s)!* A(n,s-1)
=( (n-s)!* n! ) / (n-s+1)!
=n!/(n-s+1)
只有在这些排列中,i与j之间都不会出现比i高或和i一样高的,即j对i有贡献。
p[j]
=(n!/(n-s+1))/ n!
=1/( n-s+1)

f[i] = s * p[j] = s / (n-s+1), 视野 = f[i] + 1,num[x]个人的期望视野就是num[x] * (f[i] + 1)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 302;
const int maxm = 9e4 + 10;

int n, num[1005], x, s;
double ans;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d", &x);
        num[x]++;
    }
    for(int i=1; i<=1000; i++)
    {
        if(num[i])
        {
            ans += (double)(num[i]*s)/(n-s+1) + (double)(num[i]);
        }
        s += num[i];
    }
    printf("%.2lf", ans);

    return 0;
}

 

posted @ 2022-05-18 16:26  Catherine_leah  阅读(129)  评论(0编辑  收藏  举报
/* */