题目地址:
https://www.luogu.com.cn/problem/P1595
题目描述:
某人写了
n
n
n封信和
n
n
n个信封,如果所有的信都装错了信封,求所有信都装错信封共有多少种不同情况。
输入格式:
一个信封数
n
n
n,保证
n
≤
20
n \le 20
n≤20。
输出格式:
一个整数,代表有多少种情况。
数据范围:
对于
100
%
100 \%
100%的数据,
1
≤
n
≤
20
1 \le n \le 20
1≤n≤20。
设 f [ n ] f[n] f[n]是有 n n n个信封的时候的错排数,则 f [ 1 ] = 0 , f [ 2 ] = 1 f[1]=0, f[2]=1 f[1]=0,f[2]=1。考虑 f [ n ] f[n] f[n],分步骤做,先将第 1 1 1个信封放在第 k k k个位置,那么有 n − 1 n-1 n−1种放法(不能放 1 1 1号位,别的位置都可以),接下来考虑第 k k k个信封放在了哪儿,第一种情况,它可以放在 1 1 1号位,那么剩余位置是 n − 2 n-2 n−2个元素的错排,方案数为 f [ n − 2 ] f[n-2] f[n−2];第二种情况,它可以放在除了 1 1 1号位以外的位置,那么可以将这个 k k k号信封也视为 1 1 1号信封,则方案数为 n − 1 n-1 n−1个元素的错排,即 f [ n − 1 ] f[n-1] f[n−1];由加法原理,固定 k k k后,方案数为 f [ n − 2 ] + f [ n − 1 ] f[n-2]+f[n-1] f[n−2]+f[n−1],再由乘法原理, f [ n ] = ( n − 1 ) ( f [ n − 2 ] + f [ n − 1 ] ) f[n]=(n-1)(f[n-2]+f[n-1]) f[n]=(n−1)(f[n−2]+f[n−1])代码如下:
#include <iostream>
using namespace std;
const int N = 30;
int n;
long f[N];
int main() {
scanf("%d", &n);
f[1] = 0, f[2] = 1;
for (int i = 3; i <= n; i++)
f[i] = (i - 1) * (f[i - 1] + f[i - 2]);
printf("%ld\n", f[n]);
}
时空复杂度 O ( n ) O(n) O(n)。