Examining the Rooms
- problem
- solution
- code
problem
hdu 3625
solution
之前考试有一道题:最多砸开 kkk 扇门,采取最有操作,求把 nnn 个门都打开的方案数。
本题稍稍多了一个不能砸开第一扇门的限制,以及求的是概率。
概率好说,可行方案数除以总方案数即可,总方案数显然是 nnn 把钥匙随便放的全排列,n!n!n!。
先不考虑不能砸开第一扇门,即暴力开启最多 kkk 扇门就能开启所有门。
注意到一扇门打开后只放着一把钥匙,如果不是暴力开启的门的钥匙,那么一定会开启另一扇门。
这可以对应到图上的一条边,而最后暴力开启门的钥匙就将这个形状连成一个环。
且环环之间相互独立。【只有一把钥匙一扇门】
这就相当于是将 nnn 扇门划分成最多 kkk 个圆排列的方案数。
熟悉的同学立马反应过来这就是第一类斯特林数的定义。
s(n,k)=s(n−1,k−1)+s(n−1,k)∗(n−1)s(n,k)=s(n-1,k-1)+s(n-1,k)*(n-1)s(n,k)=s(n−1,k−1)+s(n−1,k)∗(n−1)
s(n−1,k−1)→ns(n-1,k-1)\rightarrow ns(n−1,k−1)→n 自己单独新增一个圆排列。
s(n−1,k)∗(n−1)s(n-1,k)*(n-1)s(n−1,k)∗(n−1) 将 nnn 放到前面 kkk 个原排列中的某一个中去。
相当于是断开一条边,u→v⇒u→n→vu\rightarrow v\Rightarrow u\rightarrow n\rightarrow vu→v⇒u→n→v,nnn 放任意一个位置都对应着不同的圆排列,即不同的钥匙方案数。
一共可以放 n−1n-1n−1 个位置,方案数就要 ×(n−1)\times (n-1)×(n−1)。
现在加上不能暴力打开第一扇门的限制。
这个限制其实就是限制不能让第一扇门单独成为一个大小为 111 的圆排列。
如果是 nnn 个划分成 kkk 个圆排列,就得是 s(n,k)−s(n−1,k−1)s(n,k)-s(n-1,k-1)s(n,k)−s(n−1,k−1)。
求前缀和最后除以阶乘即是所求。
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 25
#define int long long
int T, n, k;
int fac[maxn];
int dp[maxn][maxn];
signed main() {dp[0][0] = fac[0] = 1;for( int i = 1; i < maxn;i ++ )for( int j = 1; j < maxn;j ++ )dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j] * ( i - 1 );for( int i = 1; i < maxn;i ++ ) fac[i] = fac[i - 1] * i;scanf( "%lld", &T );while( T -- ) {scanf( "%lld %lld", &n, &k );int ans = 0;for( int i = 0; i <= k;i ++ ) ans += dp[n][i];for( int i = 0;i < k;i ++ ) ans -= dp[n - 1][i];printf( "%.4f\n", 1.0 * ans / fac[n] );}return 0;
}