# 动态规划：Swift实现

Cruise_Chan

## 中心思想

1. 刻画一个最优解的结构特征
2. 递归地定义最优解
3. 计算最优解的值，这里一般用自底向上的方法
4. 利用计算出的信息构造一个最优解

## 举个栗子

### 栗子1）钢条切割

#### 思路

rn = max(pn, r1 + rn-1 + r2 + rn-2, ..., rn-1 + r1)

#### 自顶向下的递归实现 （问题规模由大变小）

``````CUT-ROD(p, n)
if n == 0
return 0
q = -∞
for i = 1 to n
q = max(q, p[i] + CUT-ROD(p, n-i))
return q
``````

#### 使用动态规划求解

• 带备忘的自顶向下法(top-down with memoization)。自然递归（就是正常人的想法，大问题变小问题）编写。每个过程保存一个子问题的解。在解的过程判断此子问题是否已经解过。
• 自底向上法(bottom-up method)。将子问题按规模排序，按由小至大的顺序进行求解。当解某个子问题所涉及更小的问题都已经解答完毕，可以直接使用（不用做判断是不是解答过了，省略了if判断）。

``````MEMOIZED-CUT-ROD(p, n)
let r[0..n] be a new array
for i = 0 to n
r[i] = -∞
return MEMOIZED-CUT-ROD-AUX(p, n, r)

MEMOIZED-CUT-ROD-AUX(p, n, r)
if r[n] >= 0
return r[n]
if n == 0
q = 0
else q = -∞
for i = 1 to n
q = max(q, p[i] + MEMOIZED-CUT-ROD-AUX(p, n-i, r))
r[n] = q
return q
``````

``````BOTTOM-UP-CUT-ROD(p, n)
let r[0..n] be a new array
r[0] = 0

for j = 1 to n
q = -∞
for i = 1 to j
q = max(q, p[i] + r[j-i])
r[j] = q
return r[n]
``````

#### 重构解

``````EXTENDED-BOTTOM-UP-CUT-ROD(p, n)
let r[0..n] and s[0..n] be new array
r[0] = 0
for j = 1 to n
q = -∞
for i = 1 to j
if q < p[i] + r[j-i]
q = p[i] + r[j-i]
s[j] = i
r[j] = q
return r and s

PRINT-CUT-ROD-SOLUTION(p, n)
(r, s) = EXTENDED-BOTTOM-UP-CUT-ROD(p, n)
while n > n
print s[n]
n = n - s[n]
``````

#### Swift实现

``````func extendedBottomUpCutRod(priceList: [Int], rotLength: Int) -> (result: [Int], solution: [Int]) {
var result = Array (count: rotLength + 1 , repeatedValue: 0 )
var solution = Array (count: rotLength + 1 , repeatedValue: 0 )

for var j = 1; j <= rotLength; ++j {
var optNext = -10000
for var i = 1; i <= j; i++ {
if optNext < priceList[i] + result[j - i] {
optNext = priceList[i] + result[j - i]
solution[j] = i
}
}

result[j] = optNext
}

return (result, solution)
}

func printCutRodSolution(priceList: [Int], var rotLength: Int) {
var result: [Int]
var solution: [Int]
(result, solution) = extendedBottomUpCutRod(priceList, rotLength)

while rotLength > 0 {
println(solution[rotLength])
rotLength = rotLength - solution[rotLength]
}
}

var priceArray: [Int] = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30]

printCutRodSolution(priceArray, 4)
``````

### 栗子2）最长公共子序列

#### 思路

DP问题步骤1：刻画一个最优解的结构特征

LCS最优子结构的特征如下：

1. 如果xm = yn, 那么zk = xm = yn并且Zk-1是Xm-1和Yn-1的一个LCS。
2. 如果xm != yn, 那么zk != xm意味着Z是Xm-1和Y的一个LCS。【注：因为LCS中必定不包含xm，问题规模缩减，X序列成员剔除xm，下同】
3. 如果xm != yn, 那么zk != yn意味着Z是X和Yn-1的一个LCS。

DP问题步骤2：求递归解

DP问题步骤3：自底向上计算最优解

DP问题步骤4：给出最优解 上伪代码

``````LCS-LENGTH(X, Y)
m = X.length
n = Y.length

let b[1..m, 1..n] and c[0..m, 0..n] be new tables
for i = 1 to m // 不从0开始的原因，写一个二维矩阵就懂了
c[i, 0] = 0
for j = 0 to n
c[0, j] = 0

for i = 1 to m
for j = 1 to n
if xi == yi
c[i, j] = c[i-1, j-1] + 1
b[i, j] = left-top  // left-top枚举值
elseif c[i-1, j] >= c[i, j-1]
c[i, j] = c[i-1, j]
b[i, j] = top       // top枚举值
else
c[i, j] = c[i, j-1]
b[i, j] = left      // left枚举值

return c and b
``````

``````PRINT-LCS(b, X, i, j) // 公共子序列X,Y皆可
if i == 0 or j == 0
return
if b[i, j] == left-top
PRINT-LCS(b, X, i-1, j-1)
print xi
elseif b[i, j] = top
PRINT-LCS(b, X, i-1, j)
else
PRINT-LCS(b, X, i, j-1)
``````

#### Swift实现

``````// c[i, j]与c[i-1, j-1]，c[i-1, j]，c[i, j-1]三者之间的关系枚举
enum LCSDirection: Int {
case left_top = 0
case top = 1
case left = 2
}

// 二维数组
struct Matrix {
let rows: Int, columns: Int
var grid: [Int]

init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: -10000)
}

func indexIsValidForRow(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}

subscript(row: Int, column: Int) -> Int {
get {
assert(indexIsValidForRow(row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValidForRow(row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}

func LCSLength(var rowArray: [Int], var columnArray: [Int]) -> (b: Matrix, c: Matrix){
var m = rowArray.count
var n = columnArray.count

var b: Matrix = Matrix(rows: m+1, columns: n+1)
var c: Matrix = Matrix(rows: m+1, columns: n+1)

for var i = 1; i <= m; i++ {
c[i, 0] = 0
}

for var j = 0; j <= n; j++ {
c[0, j] = 0
}

for var i = 1; i <= m; i++ {
for var j = 1; j <= n; j++ {
if rowArray[i-1] == columnArray[j-1] {
c[i, j] = c[i-1, j-1] + 1
b[i, j] = LCSDirection.left_top.rawValue
} else if c[i-1, j] >= c[i, j-1] {
c[i, j] = c[i-1, j]
b[i, j] = LCSDirection.top.rawValue
} else {
c[i, j] = c[i, j-1]
b[i, j] = LCSDirection.left.rawValue
}
}
}

return (b, c)
}

func printLCS(b: Matrix, rowArray: [Int], rowIndex: Int, columnIndex: Int) {
if rowIndex == 0 || columnIndex == 0 { return }

if b[rowIndex, columnIndex] == LCSDirection.left_top.rawValue {
printLCS(b, rowArray, rowIndex-1, columnIndex-1)
println(rowArray[rowIndex-1])
} else if b[rowIndex, columnIndex] == LCSDirection.top.rawValue {
printLCS(b, rowArray, rowIndex-1, columnIndex)
} else {
printLCS(b, rowArray, rowIndex, columnIndex-1)
}
}

var A: [Int] = [1, 2, 3, 2, 4, 1, 2]
var B: [Int] = [2, 4, 3, 1, 2, 1]

var bRecord: Matrix
var cCommon: Matrix

(bRecord, cCommon) = LCSLength(A, B)
printLCS(bRecord, A, A.count, B.count)
``````

### 栗子3）最优二叉搜索树

##### 青楼烟雨
self.blog = [note copy];

726 声望
69 粉丝
0 条评论