题目链接
https://www.luogu.org/problem/P1595
题目描述
某人写了n封信和n个信封,如果所有的信都装错了信封。求所有信都装错信封共有多少种不同情况。
输入格式
一个信封数n(n<=20)
输出格式
一个整数,代表有多少种情况。
思路如下
n封信放进n个信封,总的方案数 = n!
令F(n)为n封信放入n个信封全放错的方案数,则n封信中有m个信封放错的方案数为:
C(m,n)×F(m)
其中C(m,n)为从n封信中选出m个放错的信和信封的方案数,F(m)为m个信全部放错信封的方案数。
n封信放进n个信封,总的方案包含以下方案:
0封信放错信封 方案数 = F(0)=1
2封信放错信封 方案数 = C(2,n)×F(2)
3封信放错信封 方案数 = C(3,n)×F(3)
4封信放错信封 方案数 = C(4,n)×F(4)
· · · · · ·
n封信放错信封 方案数 = F(n)
∴F(0)+ C(2,n)×F(2)+ ···+C(n-1,n)×F(n-1)+F(n)=n!
∴F(n)=n! - F(0)- C(2,n)×F(2)+ ···+C(n-1,n)×F(n-1)
即只要已知 F(0)、F(2)···、F(n-1),即可求得F(n)。推导到这里,这道题就可以用递推了:
由F(0)、F(2)、F(3)求得F(4),再由F(0)、F(2)、F(3)、F(4)求得F(5),再由这些求得F(6)······直到求得F(n)。
代码如下:
#include<bits/stdc++.h> #include<math.h> #include<algorithm> using namespace std; long long fac(int x) { register int i; long long f = 1; for (i = 1;i <= x;i++) f *= i; return f; } int f(int m, int n) { int i, j; int ans = 1; if (m < n - m) m = n - m; for (i = m + 1; i <= n; i++) ans *= i; for (j = 1; j <= n - m; j++) ans /= j; return ans; } int main() { int i, j, n, a[20],b=0,c; cin >> n; if (n == 1) cout << 0; else { a[0] = 1;a[1] = 2; for (i = 3;i <= n - 1;i++) { b = 0; c = 1; for (j = i - 1;j >= 1;j--) { b += f(c, i + 1)*a[j - 1]; c++; } a[i - 1] = fac(i + 1) - b - 1; } cout << a[n - 2]; } return 0; }