hdu 3625
Posted 2018slgys
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu 3625相关的知识,希望对你有一定的参考价值。
题目链接
题目概述
????????每个仓库里面存放了一把钥匙(可能是打开这个仓库的,也可能是打假设最多可以强制打开(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("
");
}
}
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
", 1.0 * ans[n][m] / f[n]);
}
return 0;
}
补充
以上是关于hdu 3625的主要内容,如果未能解决你的问题,请参考以下文章
HDU 3625 Examining the Rooms(第一类斯特林数)
hdu 3625 Examining the Rooms —— 第一类斯特林数