从题意来看,就是求 n 个人的错排方案数。
考虑递推的方法:
n
n
n 个人错排,
n
n
n 一定不在自己位置上,考虑
n
n
n 站在 其它
n
−
1
n - 1
n−1 个人中某一个人的位置,显然这样能将所有情况分成不相交的情况。
假设
n
n
n 站在了
i
i
i 的位置上,那么根据
i
i
i 是否站在
n
n
n 的位置上可以再将情况分成两种:
1:
i
i
i 不在
n
n
n 的位置上,这相当于
n
−
1
n - 1
n−1个数错排,
i
i
i 不能在
n
n
n 的位置。
2:
i
i
i 在
n
n
n 的位置上,那么剩下
n
−
2
n - 2
n−2个数错排即可
因此递推式为:
d
[
n
]
=
(
n
−
1
)
∗
(
d
[
n
−
1
]
+
d
[
n
−
2
]
)
d[n] = (n - 1) * (d[n - 1] + d[n - 2])
d[n]=(n−1)∗(d[n−1]+d[n−2]),边界值为:
d
[
0
]
=
d
[
1
]
=
0
,
d
[
2
]
=
1
d[0] = d[1] = 0,d[2] = 1
d[0]=d[1]=0,d[2]=1
这个递推式是非常系数的递推式
另外一种做法是用容斥原理,
n
n
n 个人任意排列:方案有
n
!
n!
n!。
扣掉一个人站在自己的位置上,其它
n
−
1
n - 1
n−1个人任意排列:
n
!
−
C
(
n
,
1
)
∗
(
n
−
1
)
!
n! - C(n,1) * (n - 1)!
n!−C(n,1)∗(n−1)!
…
很容易得到容斥计算公式:
∑
i
=
0
n
(
−
1
)
i
C
(
n
,
i
)
(
n
−
i
)
!
\sum_{i = 0}^n(-1)^iC(n,i)(n-i)!
∑i=0n(−1)iC(n,i)(n−i)!
学了二项式反演之后,然后现在又有了一种憨憨做法:
设
f
(
i
)
f(i)
f(i)为 恰有
i
i
i 个人错排,
F
(
i
)
F(i)
F(i) 为最多 i 个人错排,显然
F
(
i
)
=
i
!
F(i) = i!
F(i)=i!。
F
(
n
)
=
∑
i
=
0
n
C
(
n
,
i
)
f
(
i
)
F(n) = \sum_{i = 0}^nC(n,i)f(i)
F(n)=∑i=0nC(n,i)f(i)
反演一下有
f
(
n
)
=
∑
i
=
0
n
(
−
1
)
n
−
i
C
(
n
,
i
)
F
(
i
)
=
∑
i
=
0
n
(
−
1
)
n
−
i
C
(
n
,
i
)
i
!
f(n) = \sum_{i = 0}^n(-1)^{n-i}C(n,i)F(i) =\sum_{i = 0}^n(-1)^{n-i}C(n,i)i!
f(n)=∑i=0n(−1)n−iC(n,i)F(i)=∑i=0n(−1)n−iC(n,i)i!
形式上和容斥原理相同。
那么还是给个递推的代码吧(逃
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
ll dp[maxn];
int main() {
dp[0] = dp[1] = 0;dp[2] = 1;
for(int i = 3; i <= 20; i++)
dp[i] = (i - 1) * (dp[i - 1] + dp[i - 2]);
int n;
while(scanf("%d",&n) != EOF) {
printf("%lld\n",dp[n]);
}
return 0;
}