码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 3625

时间:2020-07-10 17:02:16      阅读:58      评论:0      收藏:0      [点我收藏+]

标签:必须   mes   --   typedef   递推   想法   随机选择   ||   strong   

技术图片

题目链接

hdu 5625

题目概述

????????每个仓库里面存放了一把钥匙(可能是打开这个仓库的,也可能是打假设最多可以强制打开\(k\)个仓库然后取到里面的钥匙,并且要求不能强制打开1号仓库,那么计算可以把所有仓库都打开的概率,保留4位小数.

一点想法

????????首先考虑这样一个问题,就是\(n\)个仓库,如何能够打开\(k\)个仓库,拿到里面的钥匙,然后可以打开剩下其它的仓库的,很明显这个是当这\(n\)个仓库组成\(k\)个圆排列,然后从每个圆排列中暴力打开一个,取得钥匙,然后这个圆排列上的仓库都可以打开(此时每一个仓库放置的是它下一个仓库的钥匙),这个对应的就是第一类Stirling数.但是这个题目有一个要求,不能暴力打开1号仓库,所以1号仓库的钥匙只能这样放置,从\(n-1\)个仓库组成的\(k\)个排列中随机选择一个仓库放置1号钥匙,都可以通过先打开其他的仓库然后获得1号仓库的钥匙;但是对于\(n-1\)个仓库已经排成\(k-1\)个排列的情况,此时1号必须把它的钥匙放置到自身,但是自身又不能暴力打开,所以对于\(n\)个仓库不能暴力打开1号,至多先暴力打开\(k\)个仓库获得钥匙然后打开其他仓库以便全部打开的要求,在已经计算出的第一类\(Stirling\)s数\(s(n,k)\)上,要减去1号放置自身的情况,也就是\(s(n,k)-s(n-1,k-1)\).

????????上面计算的是能够打开并且保证1号不被暴力打开的可能方法数,随机放置钥匙的总的方法数是\(n!\),所以要计算概率,只要将可行的方法数除以总数就可以了,因为题目中要求的是至多打开\(k\)次,所以意味着使用\(1,2,\dots,k-1\)也要算作到至多\(k\)次里面.

注意

????????我当时算第一类\(Stirling\)数的时候考虑到了不能算完一项后就直接减,因为递推式的计算依赖于前面两项,所以我就在算完整个后去减前面的项.是的,没错,就是那么憨-_-||,忘记了递推式后面的依赖于前面的,所言前面几个因为是0计算没改变,但是一旦遇到非0的,那么前面的结果就开始改变,对应后面的也开始改变,于是越变越离谱,交上去总是WA┭┮﹏┭┮.

代码实现

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 30;
ll s[N][N];
ll ans[N][N];
ll f[N];

void calculate(){
    s[0][0] = 1;
    f[0] = 1;
    // 计算第一类Stirling数
    for (int i = 1; i < N; i++){
        for (int j = 0; j <= i; j++){
            // 第一类Stirling数递推式
            s[i][j] = (i-1) * s[i - 1][j] + s[i-1][j-1];
        }
        f[i] = f[i - 1] * i;
    }
    // 不能这样剔除,前面的改变后会影响后面剔除的结果。
    // for (int i = 1; i < N; i++){
    //     for (int j = 1; j <= i; j++){
    //         // 剔除没法打开1的情况
    //         s[i][j] -= s[i-1][j-1];
    //     }
    // }
    for (int i = 0; i < N; i++){
        for (int j = 1; j <= i; j++){
            ans[i][j] = ans[i][j - 1] + s[i][j] - s[i-1][j-1];
        }
    }
}

void print(){
    for(int i = 0; i < N; i++){
        for(int j = 0; j <= i; j++){
            printf("%lld ", s[i][j]);
        }
        printf("\n");
    }
}
int main(int argc, const char** argv) {
    calculate();
    // print();
    int t;
    scanf("%d", &t);
    while (t--){
        int n, m;
        scanf("%d%d", &n, &m);
        printf("%.4f\n", 1.0 * ans[n][m] / f[n]);
    }
    return 0;
}

补充

hdu 3625

标签:必须   mes   --   typedef   递推   想法   随机选择   ||   strong   

原文地址:https://www.cnblogs.com/2018slgys/p/13279928.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!