1. 题面
2. 解法
- 错排公式
将(N)个编号元素放在(N)个编号位置,求元素编号和位置编号不相同的方案数。这样的问题我们称为错排问题
递推公式$$D[n] = (n-1)*[D[n-1]+D[n-2]]$$
其中(D[n])表示方案数
下面给出证明:
第一步,把第(n)个元素放在一个位置上,有(n-1)种放法
第二步,放第(k)个元素时,有两种情况。(1)放在第n号位置时,剩下的(n-2)个元素有(D[n-2])种方法。(2)不放在n号位置时,这(n-1)个元素有(n-1)种放置方法。
由加法原理和乘法原理得:
[D[n] = (n-1)*[D[n-1]+D[n-2]]
]
接下来的事情就非常简单了,只需要递归计算(D[n]),然后除(n!)即可。
#include<bits/stdc++.h>
using namespace std;
unsigned long long fact[21];
unsigned long long dfs(int n)
{
if(n == 1) return 0;
if(n == 2) return 1;
return (n - 1) *(dfs(n - 2) + dfs(n - 1));
}
int main()
{
int cnt,t;
fact[1] = 1;
for(int i = 2;i <= 20;i ++ )
{
fact[i] = fact[i - 1] * i;
}
cin >> t;
for(int i = 1;i <= t;i ++ )
{
cin >> cnt;
cout << fixed << setprecision(2) << dfs(cnt) * 1.0 / fact[cnt] * 100 << "%" << endl;
}
return 0;
}
- 容斥原理
正整数(1, 2, 3,cdots, n)的全排列有(n!)种,其中第(k)位是(k)的排列有((n-1)!)种;当(k)分别取(1, 2, 3,cdots, n)时,共有(n*(n-1)!)种排列是至少放对了一个的,由于所求的是错排的种数,所以应当减去这些排列;但是此时把同时有两个数不错排的排列多排除了一次,应补上;在补上时,把同时有三个数不错排的排列多补上了一次,应排除;(cdotscdots);继续这一过程,得到错排的排列种数为
[D(n) = n! - frac{n!}{1!} + frac{n!}{2!} - frac{n!}{3!} + cdots + frac{{-1}^{n}*n!}{n!} = sum_{k=2}^{n}frac{{-1}^{k} * n!}{k!}
]
#include<bits/stdc++.h>
using namespace std;
int n;
long long b[21];
int main()
{
b[1] = 1;
for(int i = 2;i <= 20;i ++ )
{
b[i] = b[i - 1] * i;
}
int t;
cin >> t;
for(int i = 1;i <= t;i ++ )
{
long long sum = 0;
cin >> n;
for(int k = 2;k <= n;k ++ )
{
sum += (pow(-1,k) * b[n]) / b[k];
}
cout << fixed << setprecision(2) << ((sum * 1.0) / b[n]) * 100 << "%" << endl;
}
return 0;
}
- 错排公式
这个东西是
[D(n)= [frac{n!}{e}+0.5]
]
具体怎么证明我也不太清楚,高数还没学到,贴一下百度百科上的证明好力(
证明:
由于$$frac{1}{e} = e^{-1} = frac{1}{0!} - frac{1}{1!} + frac{1}{2!} - frac{1}{3!} - cdots + frac{{-1}^{n}}{n!} + Rn(-1)$$,
其中(Rn(-1))是余项,等于(frac{{-1}^{n+1} * e^u}{(n+1)!}),且(u in (-1, 0)).
所以,$$D(n) = n! * {e}^{-1} - frac{{-1}^{n+1} * e^u}{(n+1)},u∈(-1, 0)$$
而$$|n! Rn| = |frac{{-1}^{n+1} * {e}^{u}}{n+1}| = frac{{e}^{u}}{n+1} in (frac{1}{[e(n+1)]}, frac{1}{(n+1)})$$可知即使在(n=1)时,该余项(的绝对值)也小于(frac{1}{2})。
因此,无论(n! Rn)是正是负,(frac{n!}{e} + frac{1}{2})的整数部分都一定与M(n)相同。
#include<bits/stdc++.h>
using namespace std;
#define e 2.718281828459
int main()
{
int n,m,t;
double a[21] = {1,1,2,6};
long long b[21] = {0,0,1,2};
for(int i = 4;i <= 20;i ++ )
{
a[i] = a[i - 1] * i;
b[i] = (a[i]/e + 0.5);
}
cin >> t;
for(int i = 1;i <= t;i ++ )
{
cin >> n;
printf("%.2lf%%
",b[n]/a[n]*100);
}
return 0;
}
4. 后记
哇,这篇写了超长时间,从开始写这个题,然后寻找其他解法,再然后写各种代码,简直是我写过最长时间的一篇了。