洛谷【P1595 信封问题】 题解

时间:2019-08-19
本文章向大家介绍洛谷【P1595 信封问题】 题解,主要包括洛谷【P1595 信封问题】 题解使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目链接 

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;
}

原文地址:https://www.cnblogs.com/lau1997/p/11376699.html