兩個問題的對比

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求解.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章