两个问题的对比

来源:互联网 发布:校园网络连接 编辑:程序博客网 时间:2024/05/16 12:26

0 问题描述

问题1(CF390C): 给定若干行聊天记录 “发言人: 话”, 有些聊天记录的”发言人”是缺失的, 已知相邻对话发言人一定不同, 且每个发言人说的话里都不会包含自己的名字, 现给定可能的发言人的集合, 请填充每个对话的发言人.(来自Codeforces Round 390: http://codeforces.com/contest/754/problem/C)

问题2(LC36): 给定一个9*9的矩阵, 有些元素为空, 要求使用1-9填充空元素, 使每行, 每列, 每个3*3小矩阵(共9个)中都包含1-9这9个数字.(来自 leetcode: https://leetcode.com/problems/sudoku-solver/?tab=Description)

先说结论, 两个问题非常相似, 但是第一个问题可用动态规划求解; 由于状态转移方程极难表述, 第二个问题不能用动态规划求解

1 用一张表表述问题

对于LC36, 在初始条件下, 如果我们不对矩阵进行任何填充, 那么问题的当前状态可以用一张表进行描述, 注意, 我们可以按照从上到下,从左到右的方式遍历矩阵的每个元素, 这样我们用一维序列描述二维矩阵:

元素编号 可能的填充数字 1 a11,a12,... 2 a21,... … … 81 a81,1,a81,2,...

同样地, 对于CF390C, 在我们填写发言人之前, 问题的初始状态也可以用一张表描述:

聊天记录 可能的发言人 第1条记录 b11,b12,... 第2条记录 b21,... … … 第n条记录 bn1,...

一旦我们开始填写矩阵/发言人, 那么上述两张表的状态就会改变, 而且改变总是会让每行的”候选集”缩小.即, 我们列出的每行的候选集一定是最终结果的超集.

2 使用动态规划描述问题

在LC36的题意下, (i,j)表示填第i个元素, 使用数字j, 然后问题跳转到(i+1,j)
在390C的题意下, (i,j)表示填第i个对话, 使用第j个人, 然后问题跳转到(i+1,j)

乍看上去, 这两个问题是一样的. 可是我们遗漏了一很关键的东西, 就是我们对(i+1,j)的表述是不完整的.因为我们无论何时, 只要我们填写了i, 那么在我们填写i+1时就会受到我们刚才的填写行为的约束. 即问题i的决策会对问题i+1产生影响, 但是我们并没有表述出这种影响.

3 重新描述问题

对于LC36:

  1. (i,j)表示第i个格子, 使用j
  2. 问题跳转到(i+1), 引入额外的约束:”不能使用j”, 为了表述额外约束, 我们需要修改问题的表示形式
  3. 于是我们返回去重新表述(i): 填写第i个格子, 使用数字j, 不能使用的数字列表为k, 记为(i,j,k)
  4. 问题再次跳转到(i+1): 填写第i+1个格子, 使用数字j, 不能使用的数字列表为k,j, 记为(i+1,j,[k,j])

这样可以构造DP问题[y,x,i,M] 表示在不能使用数字的屏蔽码为M的情况下用i台填写格子(y,x),并且不会破坏矩阵性质的可能性(可能/不可能), 复杂度是: 9*9*9*512 < 1000 * 600 = 600, 000, 貌似可解

而对于390C:

  1. (i,j)表示, 填写第i行, 使用人物j
  2. 然后跳转到(i+1), 问题表述为填写第i+1行, 使用人物j, 不能使用人物j, 引入新的约束, 需重新表示问题
  3. 重新表述i为, 填写第i行, 使用人物j, 不能使用人物k, 记为(i,j,k)
  4. 再次跳转到i+1, 问题表述为填写i个格子, 使用人物j, 不能使用人物j, 记为(i+1,j,j)

至此, 可以构造DP问题[i,j,k], 表示从第i个人开始填写, 使用j号人物, 不能使用k号人物, 最终可以成功填写的可能性(可能/不可能).

4 分析

这两个问题再以下方面是相同的:

  1. 都是一个一个的填写, 并且每个都有个候选列表
  2. 填写一个以后, 会影响其他人的填写

但是CF390C和LC36相比有个关键的不同点: 前者跳转时, 会不携带来自(i1)的约束, 而后者会携带来自(i1)的约束:

  1. 对于CF390C, 问题的跳转方式是(i,j,k)(i+1,j,j), (i+1)的第三个状态与i的第三个状态无关.
  2. 对于LC36, 问题的跳转方式是(i,j,[k])(i+1,j,[k,j]), (i+1)的第三个状态与i的第三个状态有关.

至此, 我们发现第一个是普通的动态规划, 第二个似乎是状态压缩动态规划.可是真是这样吗?

对于CF390C, 我们在计算(i,j,k)时可以很方便地算出(i+1)的两个状态参数:

  • j: 遍历每个可能的发言人
  • j: 直接使用问题i的第二个状态填充

然而, 对于LC36, 我们在计算(i,j,k)时却很难算出(i+1)的第三个参数!我们来看第三个参数的意义:不能使用的数字集合.这个参数的计算过程是这样的: 一方面, 如果i号元素是定值, 那么第三个参数k也是定值, 直接计算;另一方面, 如果i号元素为空, 那么第三个状态k的计算要考虑如下因素:

  1. 元素在矩阵中受到的天然限制: 行, 列, 小3*3小矩阵中的元素.这个限制就是第1节中的表的限制.
  2. 元素受到i的限制: 父元素i有自己的不可用数字集合, 这个集合中的数字来自于父元素i的表, 以及父元素i的父元素(i1).

这样一来, 如果你要计算(i+1)的第三个参数, 必须清楚地知道, 父元素不可用数字集中每个数字是如何生成的:

  1. 对于受到原始限制的数字, 我们可以通过ii+1的想对位置, 进行修正. 比如, 若i(i+1)在同一行,且在同一个小矩阵中 那么我们从i的原始限制集中去掉通过列限制提供的数字.
  2. 对于受到i的先前元素限制的数字, 我们必须找到他们的生成位置, 然后根据生成位置和(i+1)的想对位置, 来计算(i+1)的不可用数字集合.

稍加思考就会知道, 上述两个步骤, 每一步都是几乎不可实现的.因此, LC36虽然和CF390C非常像, 但是后者可以用DP求解, 但是前者很难用DP求解.

1 0
原创粉丝点击