zoukankan      html  css  js  c++  java
  • hihoCoder #1159 扑克牌

    很早(大概两年前)就思考过这道题,然而当时并未解出。最近又把这道题翻出来,仍是看了题解才略知解法大义。现在我把这道题的解法以及我解题过程中的波折较详细地写下来,供后来人参考。


    题目大意

    一副不含王的扑克牌由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] $$

  • 相关阅读:
    java回顾之多线程
    java回顾之异常
    模拟斗地主和冒泡排序
    java回顾之Map
    java回顾之集合List
    java回顾之树
    java回顾之单列集合、泛型、数据结构
    java回顾之类API再体验之引用类型小结
    java回顾之API初体验
    函数之 闭包函数 和 装饰器
  • 原文地址:https://www.cnblogs.com/Patt/p/7624916.html
Copyright © 2011-2022 走看看