题目地址:
https://leetcode.com/problems/find-the-derangement-of-an-array/
求 n n n个元素的错排数。
法1:动态规划。设 f [ n ] f[n] f[n]是 n n n元错排数。设 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an是个错排,那么 a n ≠ n a_n\ne n an=n,如果 a k = n a_k=n ak=n,我们考虑 k k k放在了哪儿。如果 a n = k a_n=k an=k,那么其余的数的错排方案数就是 f [ n − 2 ] f[n-2] f[n−2];如果 a n ≠ k a_n\ne k an=k,那么对于 n − 1 n-1 n−1个数的每一个错排,都可以将这个错排中的 a k a_k ak换到 a n a_n an那里去,所以此时有 f [ n − 1 ] f[n-1] f[n−1]个错排。综上 f [ n ] = ( n − 1 ) ( f [ n − 1 ] + f [ n − 2 ] ) f[n]=(n-1)(f[n-1]+f[n-2]) f[n]=(n−1)(f[n−1]+f[n−2])初始条件 f [ 1 ] = 0 f[1]=0 f[1]=0,注意 f [ 0 ] f[0] f[0]应该取 1 1 1,错排的定义是 ∀ a i , a i ≠ i \forall a_i,a_i\ne i ∀ai,ai=i,这对于空集是成立的。代码如下:
public class Solution {
public int findDerangement(int n) {
int MOD = (int) (1E9 + 7);
long[] dp = new long[n + 1];
dp[0] = 1;
dp[1] = 0;
for (int i = 2; i <= n; i++) {
dp[i] = (i - 1) * (dp[i - 1] + dp[i - 2]);
dp[i] %= MOD;
}
return (int) dp[n];
}
}
时空复杂度 O ( n ) O(n) O(n)。
考虑滚动数组优化:
public class Solution {
public int findDerangement(int n) {
int MOD = (int) (1E9 + 7);
long[] dp = new long[2];
dp[0] = 1;
dp[1] = 0;
for (int i = 2; i <= n; i++) {
dp[i & 1] = (i - 1) * (dp[i - 1 & 1] + dp[i - 2 & 1]);
dp[i & 1] %= MOD;
}
return (int) dp[n & 1];
}
}
时间复杂度不变,空间 O ( 1 ) O(1) O(1)。
法2:直接用数学公式算。有若干种办法求这个公式。这里介绍一个概率论的方法。设 A i A_i Ai为事件 a i = i a_i=i ai=i,那么相当于要求 1 − P ( ⋃ i = 1 n A i ) 1-P(\bigcup_{i=1}^{n}A_i) 1−P(i=1⋃nAi)而由容斥原理,有: P ( ⋃ i = 1 n A i ) = ∑ i P ( A i ) − ∑ i 1 < i 2 P ( A i 1 A i 2 ) + ∑ i 1 < i 2 < i 3 P ( A i 1 A i 2 A i 3 ) − . . . + ( − 1 ) n − 1 P ( A i 1 . . . A i n ) P(\bigcup_{i=1}^{n}A_i)=\sum_{i}P(A_i)-\sum_{i_1< i_2}P(A_{i_1}A_{i_2})+\\\sum_{i_1< i_2<i_3}P(A_{i_1}A_{i_2}A_{i_3})-...+(-1)^{n-1}P(A_{i_1}...A_{i_n}) P(i=1⋃nAi)=i∑P(Ai)−i1<i2∑P(Ai1Ai2)+i1<i2<i3∑P(Ai1Ai2Ai3)−...+(−1)n−1P(Ai1...Ain)由对称性, P ( A i ) = 1 n , P ( A i A j ) = 1 n ( n − 1 ) , . . . , P ( A 1 A 2 . . . A n ) = 1 n ! P(A_i)=\frac{1}{n},P(A_iA_j)=\frac{1}{n(n-1)},...,P(A_1A_2...A_n)=\frac{1}{n!} P(Ai)=n1,P(AiAj)=n(n−1)1,...,P(A1A2...An)=n!1,所以 P ( ⋃ i = 1 n A i ) = 1 − 1 2 ! + 1 3 ! − 1 4 ! + . . . + ( − 1 ) n − 1 1 n ! P(\bigcup_{i=1}^{n}A_i)=1-\frac{1}{2!}+\frac{1}{3!}-\frac{1}{4!}+...+(-1)^{n-1}\frac{1}{n!} P(i=1⋃nAi)=1−2!1+3!1−4!1+...+(−1)n−1n!1而事件的总数是 n ! n! n!,所以错排数就是 n ! ( 1 2 ! − 1 3 ! + 1 4 ! − . . . + ( − 1 ) n 1 n ! ) n!(\frac{1}{2!}-\frac{1}{3!}+\frac{1}{4!}-...+(-1)^{n}\frac{1}{n!}) n!(2!1−3!1+4!1−...+(−1)nn!1)代码如下:
public class Solution {
public int findDerangement(int n) {
if (n == 1) {
return 0;
}
if (n == 2) {
return 1;
}
int MOD = (int) (1E9+7);
long fac = 1, res = 0;
for (int i = n; i >= 0; i--) {
res = (res + fac * (i % 2 == 0 ? 1 : -1) + MOD) % MOD;
fac = (fac * i) % MOD;
}
return (int) ((res + MOD) % MOD);
}
}
时间复杂度一样,空间 O ( 1 ) O(1) O(1)。