题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1465

题目描述

大家常常感慨,要做好一件事情真的不容易,确实,失败比成功容易多了!

做好“一件”事情尚且不易,若想永远成功而总从不失败,那更是难上加难了,就像花钱总是比挣钱容易的道理一样。

话虽这样说,我还是要告诉大家,要想失败到一定程度也是不容易的。比如,我高中的时候,就有一个神奇的女生,在英语考试的时候,竟然把40个单项选择题全部做错了!大家都学过概率论,应该知道出现这种情况的概率,所以至今我都觉得这是一件神奇的事情。如果套用一句经典的评语,我们可以这样总结:一个人做错一道选择题并不难,难的是全部做错,一个不对。

不幸的是,这种小概率事件又发生了,而且就在我们身边:

事情是这样的——HDU有个网名叫做8006的男性同学,结交网友无数,最近该同学玩起了浪漫,同时给n个网友每人写了一封信,这都没什么,要命的是,他竟然把所有的信都装错了信封!注意了,是全部装错哟!

现在的问题是:请大家帮可怜的8006同学计算一下,一共有多少种可能的错误方式呢?

思路

这题有点坑啊,Wa了两次,主要是递推公式没求对,室友又坑爹,宿舍只要有人就不能安宁。

第一次得到的递推公式是,$F(n) = F(n-1)*(n-1)$,想得太简单了,当时只是考虑将一个信封放错有$n-1$种放法乘以前一种即可。

第二次得到的递推公式和最后得到的正确结果接近。就不贴错误答案了。。

最后一次得出 $F(n) = A(n,n) - C(n,1)*F(n-1) - C(n,2)*F(n-2) - ... -C(n,n-2)*F(2) - 1$

其中$F(1) = 0, F(2) = 1$

这个递推公式刚开始是顺推求不出来后面逆推得解。

首先考虑$F(n)$表示$n$个信封全放错的总数,那么$A(n,n)$表示$n$个信封一共放法的数量,然后减去一个信封放对其余信封都放错的数量$(C(n,1)*F(n-1))$,再减去两个信封都放对其余信封都放错的数量$(C(n,2)*F(n-2))$,一次类推,最后减去一种情况就是全部放对的数量(1种)。

看了下评论,给跪了,有错排公式= =
$$F(n) = (n-1) [F(n-2) + F(n-1)]$$

AC代码

/*************************************************************************
    > File Name: 1465.cpp
      > Author: Netcan
      > Blog: http://www.netcan.xyz
      > Mail: 1469709759@qq.com
      > Created Time: Mon 25 May 2015 11:37:52 PM CST
 ************************************************************************/

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <sstream>
#include <deque>
#include <functional>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <stack>
#include <set>
#include <numeric>
#include <utility>
#include <cstring>
using namespace std;

const long long fac[21]={1,1,2,6,24,120,720,5040,40320,362880,
                        3628800,39916800,479001600,6227020800,
                        87178291200,1307674368000,20922789888000,
                        355687428096000,6402373705728000,121645100408832000,
                        2432902008176640000}; // 阶乘
long long C(int n,int m) { // 组合数公式
    return fac[n]/fac[m]/fac[n-m];
}
int main()
{
#ifdef Oj
#endif
    long long f[25];
    f[1] = 0;
    for(int n=2; n<=20; ++n) {
        f[n] = fac[n] - 1;
        for(int i=n-2; i>=1; --i)
            f[n] -= C(n, i)*f[n-i];

    }
    int n;
    while(cin >> n)
        cout << f[n] << endl;

    return 0;
}