很早(大概两年前)就思考过这道题,然而当时并未解出。最近又把这道题翻出来,仍是看了题解才略知解法大义。现在我把这道题的解法以及我解题过程中的波折较详细地写下来,供后来人参考。
题目大意
一副不含王的扑克牌由52张牌组成,由红桃、黑桃、梅花、方块4组牌组成,每组13张不同的面值。现在给定52张牌中的若干张,请计算将它们排成一列,相邻的牌面值不同的方案数。
注:题目描述中的「面值」仅指点数,不包括花色。
分析
这道题是一个计数 DP 问题。这里给出两种解法,二者用不同的方法来看待(或称「考虑」)「排列给定的若干张扑克牌」这个过程。
解法一
此解法考虑「一张一张地排列给定的若干张扑克牌」的过程。
假设总共有 $n$ 张牌,已经排列了一些,还剩下 $m$ 张未排列。我们考虑「剩下的这 $m$ 张牌(接着已经排列好的那些牌)还有多少种排列方案」。
显然,答案只跟「剩下的牌」和「已排好的最末一张牌」有关。
进一步思考,关于「剩下的牌」我们需要知道哪些信息?是剩下的牌的集合吗?不是的。我们只需要知道「剩下的牌中出现了 $1$ 次、$2$ 次、$3$ 次、$4$ 次的点数分别有多少个」就够了,而无需详察每一张剩下的牌的点数或花色。相应地,关于「已排好的最末一张牌」,只消知道「其点数在剩余牌中出现了几次」,亦不必关心其究竟为何种点数或花色。据此,不难得出下述 DP 状态:
DP 状态
$mathrm{DP}[a_1][a_2][a_3][a_4][last]$ :在「已排好的最末一张牌的点数」还剩 $last$ 张($0le last < 4$),还剩 $i$ 张的点数有 $a_i$ 种($1le ile 4$)的情况下,剩下的牌还有多少种排列。
转移方程
$$
egin{align*}
mathrm{DP}[a_1][a_2][a_3][a_4][last] = & (a_1 - delta_{last,1})~mathrm{DP}[a_1 - 1][a_2][a_3][a_4][0] \
& + 2~(a_2 - delta_{last, 2})~mathrm{DP}[a_1 + 1][a_2 - 1][a_3][a_4][1] \
& + 3~(a_3 - delta_{last,3})~mathrm{DP}[a_1][a_2 + 1][a_3 - 1][a_4][2] \
& + 4a_4mathrm{DP}[a_1][a_2][a_3 + 1][a_4 -1][3]
end{align*}
$$
其中,$delta_{i,j}$ 称作 Kronecker delta,
$$
delta_{i,j} =
egin{cases}
1, & ext{if $i =j$;} \
0, & ext{if $i
e j$.}
end{cases}
$$
边界条件
$mathrm{DP}[0][0][0][0][0] = 1$
两个失败的 DP 状态设计
我照着上述思路设计 DP 状态时有两次失败的尝试,现简述如下:
为简便计,我们用四元组 $(x_1, x_2, x_3, x_4)$ 表示尚未排列的扑克牌集合。「尚未排列的扑克牌集合」下文也简称「牌集」。
给定初始牌集 $(c_1, c_2, c_3, c_4)$ 。
尝试一
$mathrm{DP}[a_1][a_2][a_3][a_4][last]$ :满足「剩余 $i$ 张的点数为 $a_i$ ($1 le i le 4$),且应经排好的牌的最后一张的点数为 $last$ 」的「排列已取出的 $sum_{1le i le 4} c_i - a_i$ 张牌」的方案数。
边界条件:$mathrm{DP}[c_1][c_2][c_3][c_4][0] = 1$ 。
这个 DP 状态的问题在于:对每个输入都需要单独计算一次方案数,不同的输入之间不存在(?)重叠子问题,因而时间复杂度过高。
尝试二
$mathrm{DP}[a_1][a_2][a_3][a_4][last]$ 表示「(按题目要求)排列 $(a_1, a_2, a_3, a_4)$ 且最后一张牌的点数在牌集中出现了 $last$ 次」的方案数。
边界条件:$mathrm{DP}[0][0][0][0][0] = 1$ 。
这种状态是无法转移的。
解法二
不难看出,扑克牌的花色是很容易处理的次要因素。在解法二中,我们将扑克牌的花色去掉,只留点数。这样,输入就可以看作 $n$ 个字符。
此解法考虑「采用每次将所有相同字符插入当前字符串这种方式来排列给定的 $n$ 个字符」的过程。
举例:给定字符串 aabbccd
,将其按上述方法重新排列。
重排 #1
aa
$ o$ abab
$ o$ cabcab
$ o$ cabdcab
或者,重排 #2
aa
$ o$ baab
$ o$ ccbaab
$ o$ cdcbaab
当然,这第二种排法不满足「任意相邻字符不相同」。
一个长为 $l$ 的字符串有 $l+1$ 个「插入位」。比如,字符串 aabbc
的插入位可标示为 a a b b c _ 。
若某个「插入位」上的字符和前一个「插入位」上的字符相同则称此「插入位」为「重复位」。aabbc
的第二个和第四个「插入位」即为「重复位」。
我们可以用二元组 $(a, b)$ 来描述一个字符串 $S$ 的状态 :$a$ 是 $S$ 包含的不同字符的个数,$b$ 是 $S$ 的「重复位」的个数。
借助上面的定义,下面给出另一种 DP 思路。
DP 状态
$mathrm{DP}[i][j]$ :排列前 $i$ 种字符使得所得字符串的「重复位」的个数为 $j$ 的方案数。
转移方程
设第 $i$ 种字符有 $c_i$ 个,前 $i$ 种字符总数为 $s_i$, 即 $s_i = sum_{1le jle i}c_j$ 。前 $i$ 种字符组成长为 $s_i$ 的字符串,有 $s_i + 1$ 个「插入位」,设其状态为 $(i,j)$ 。考虑将 $c_{i+1}$ 个第 $i+1$ 种字符分成 $k$ 组,其中 $x$ 组插入到「重复位」上,$k - x$ 组插入到非「重复位」上。不难得到下述转移方程:
$$mathrm{DP}[i][ j ] xrightarrow{dbinom{c_{i+1} - 1}{ k - 1} dbinom{j}{x} dbinom{s_i + 1 - j}{k-x} } mathrm{DP}[i+1][ j - x + c_{i+1} - k] $$