(整齐打印)考虑整齐打印问题,即在打印机上用等宽字符打印一段文本。输入文本为
n
n
n个单词的序列,单词长度分别为
l
1
,
l
2
,
…
,
l
n
l_1, l_2, …, l_n
l1,l2,…,ln个字符。我们希望将此段文本整齐打印在若干行上,每行最多
M
M
M个字符。“整齐”的标准是这样的。如果某行包含从第
i
i
i个到第
j
(
i
≤
j
)
j (i ≤ j)
j(i≤j)个的单词,且单词之间的间隔为一个空格符,则行尾的剩余空格符数量为
M
−
j
+
i
−
∑
k
=
i
j
l
k
M-j+i-\sum_{k=i}^{j}{l_k}
M−j+i−∑k=ijlk,此值必须为非负的,否则一行无法容纳这些单词。我们希望能最小化所有行的(除最后一行外)剩余空格数的立方之和。设计一个动态规划算法,在打印机上整齐打印一段
n
n
n个单词的文本。分析算法的时间和空间复杂度。
解
根据题意,单个单词是不能跨行打印的。另外为简化分析,我们还需要假设任意一个单词
k
k
k的长度不超过一行所能容纳的字符个数,即
l
k
≤
M
,
k
=
1
,
2
,
…
,
n
l_k ≤ M,k = 1, 2, … , n
lk≤M,k=1,2,…,n。对于任意一行来说,假设从单词
i
i
i开始,到单词
j
j
j结束,则该行末尾的剩余空格数为
我们用
l
i
n
e
_
c
u
b
e
s
(
i
,
j
)
line\_cubes(i, j)
line_cubes(i,j)表示一行的剩余空格数的立方和,该行从单词
i
i
i开始,到单词
j
j
j结束。
l
i
n
e
_
c
u
b
e
s
(
i
,
j
)
line\_cubes(i, j)
line_cubes(i,j)分三种情况:
1)
l
i
n
e
_
e
x
t
r
a
(
i
,
j
)
<
0
line\_extra(i, j) < 0
line_extra(i,j)<0:在这种情况下,一行容不下
i
i
i~
j
j
j的所有单词。显然,这种情况不能出现,所以把
l
i
n
e
_
c
u
b
e
s
(
i
,
j
)
line\_cubes(i, j)
line_cubes(i,j)取为无穷大;
2)
l
i
n
e
_
e
x
t
r
a
(
i
,
j
)
≥
0
line\_extra(i, j) ≥ 0
line_extra(i,j)≥0且
j
=
n
j = n
j=n:这意味着这是最后一行,由于最后一行不计算剩余空格数的立方,所以把
l
i
n
e
_
c
u
b
e
s
(
i
,
j
)
line\_cubes(i, j)
line_cubes(i,j)取为
0
0
0;
3)
l
i
n
e
_
e
x
t
r
a
(
i
,
j
)
≥
0
line\_extra(i, j) ≥ 0
line_extra(i,j)≥0且
j
<
n
j < n
j<n:这意味着这不是最后一行,有
l
i
n
e
_
c
u
b
e
s
(
i
,
j
)
=
(
l
i
n
e
_
e
x
t
r
a
(
i
,
j
)
)
3
line\_cubes(i, j) = ( line\_extra(i, j) )^3
line_cubes(i,j)=(line_extra(i,j))3。
综上所述,可以得到
我们再定义只打印单词
1
1
1 ~
j
j
j的最优化方案的所有行的剩余空格数的立方和为
c
u
b
e
s
(
j
)
cubes(j)
cubes(j)。这里需要补充说明一下:如果
j
<
n
j < n
j<n,那么
c
u
b
e
s
(
j
)
cubes(j)
cubes(j)包括最后一行的剩余空格数的立方;如果
j
=
n
j = n
j=n,则
c
u
b
e
s
(
j
)
cubes(j)
cubes(j)不计入最后一行。下面建立
c
u
b
e
s
(
j
)
cubes(j)
cubes(j)的递归式。
对于单词
1
1
1 ~
j
j
j的任意一个打印排列,单词
j
j
j自然位于最后一行的末尾,而最后一行的开始可以是1 ~
j
j
j中的任意一个单词。假设以单词
i
(
1
≤
i
≤
j
)
i (1 ≤ i ≤ j)
i(1≤i≤j)作为最后一行的开始,那么这种情况下最后一行的剩余空格数的立方为
l
i
n
e
_
c
u
b
e
s
(
i
,
j
)
line\_cubes(i, j)
line_cubes(i,j),而其余单词
1
1
1 ~
i
−
1
i-1
i−1的最优打印排列的所有行的剩余空格数的立方和为
c
u
b
e
s
(
i
−
1
)
cubes(i-1)
cubes(i−1)。将以上两项加起来,即可得单词
1
1
1 ~
j
j
j的所有行的剩余空格数的立方和,为
l
i
n
e
_
c
u
b
e
s
(
i
,
j
)
+
c
u
b
e
s
(
i
−
1
)
line\_cubes(i, j) + cubes(i-1)
line_cubes(i,j)+cubes(i−1)。将
i
i
i从
1
1
1到
j
j
j进行遍历,寻找最小的
l
i
n
e
_
c
u
b
e
s
(
i
,
j
)
+
c
u
b
e
s
(
i
−
1
)
line\_cubes(i, j) + cubes(i-1)
line_cubes(i,j)+cubes(i−1),作为单词
1
1
1 ~
j
j
j的最优打印排列的所有行的剩余空格数的立方和
c
u
b
e
s
(
j
)
cubes(j)
cubes(j)。于是可以得到以下递归式
我们最终要计算的是
c
u
b
e
s
(
n
)
cubes(n)
cubes(n)。可以用一个数组
p
[
j
]
p[j]
p[j]来记录单词
1
1
1 ~
j
j
j的最优打印排列的最后一行的起始单词。
算法NEATLY-PRINT包含两个双层循环,以及一个单层循环,它的时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。
本节相关的code链接。
https://github.com/yangtzhou2012/Introduction_to_Algorithms_3rd/tree/master/Chapter15/Problems/Problem_15-4
算法导论 — 思考题15-4 整齐打印
最新推荐文章于 2021-04-24 20:32:03 发布