信件错排问题分析(动态规划)

问题描述

有 N 个信件和信箱,每封信件对应一个正确信箱位置。现在它们被打乱,求错误装信方式的数量。保证每一封信都装在错误的位置。

思路

抽象成动态规划问题

定义一个数组dp[]存储错误方式数量。dp[i]表示,有i封信、i个信箱情况下的错误装信方法总数。

转移方程建立

对于第N封信而言,假设其装在了第 K 个信箱中,对于第 K 封信,有两种情况,(1)信件 K 装在信箱 N 中;(2)信件 K 未被装在信箱 N 中。

1. 信件K装在信箱N中

如下图所示:
image
我们不看 K 和 N 两对信件和信箱,只关注剩下 N-2 对信件和信箱,有 dp[N-2] 种错误装信方法。同时, K 的取值范围: 1~N-1 ,因此共有 (N-1)*dp[N-2] 种错误装信方法。

2. 信件 K 未被装在信箱 N 中

如下图所示:
image

先给出结果,该情况的错误装信方法有 (N-1)*dp[N-1] 种。这个 dp[N-1] 是如何得出来,我思考了良久。

我们把问题重新描述一下,思路就会清晰很多。事实上,对于信件 i 来说,信箱 i 是它的“专属信箱”,每个信件都不能放入自己的专属信箱,对于n 个信件而言,都需要找其它 n-1 个信箱放入,其错排方法数为 dp[n] 。

问题的症结在于,对于上图中的信件 K 来说,其专属信箱,即 K 信箱已经被占用。

但是,我们可以把信箱 N 当做信件 K 的“专属信箱”,因为本情况下,信件 K 也不能放入信箱 N 。所以可以理解成求 N-1 封信件和 N-1 个信箱(除去信件 N)之间的错排数量问题。所以得到 dp[N-1]。

同理 K 的取值范围: 1~N-1 ,因此共有 (N-1)*dp[N-1] 种错误装信方法。

综上,状态转移方程
image

代码

    /**
     * 动态规划-信件错排问题
     */
    private int MailMisalignment(int n){
        if(n==0) return 0;
        if(n==1) return 0;
        int[] dp = new int[n];
        dp[0] = 0;
        dp[1] = 1;
        for(int i=2;i<n;++i){
            dp[i] = (i-1)*dp[i-2] + (i-1)*dp[i-1];
        }
        return dp[n-1];
    }
posted @ 2020-07-03 11:52  数小钱钱的种花兔  阅读(1257)  评论(0编辑  收藏  举报