问题描述
给定
n
n
n 块积木,编号为
1
1
1 到
n
n
n。第
i
i
i 块积木的重量为
w
i
w_i
wi(
w
i
w_i
wi 为整数),硬度为
s
i
s_i
si,价值为
v
i
v_i
vi。
现要从中选择部分积木垂直摞成一座塔,要求每块积木满足如下条件:
若第
i
i
i 块积木在积木塔中,那么在其之上摆放的所有积木的重量之和不能超过第
i
i
i 块积木的硬度。
试设计算法求出满足上述条件的价值和最大的积木塔,输出摆放方案和最大价和。
算法思想
相关数学结论证明
先寻找能简化问题的数学结论:任意 k k k块积木,若存在能够堆叠成塔的方案,则可行方案中一定包含它们按 s i + w i s_i+w_i si+wi递增的顺序自上而下地堆叠的方案,且该方案一定是最优方案。这里的“最优”定义为:将 k k k块积木视为一个整体的承重能力最强。
- 首先证明两块积木的情况:对两块积木
(
s
1
,
w
1
)
,
(
s
2
,
w
2
)
(s_1,w_1),(s_2,w_2)
(s1,w1),(s2,w2),不妨设
s
1
+
w
1
≤
s
2
+
w
2
s_1+w_1\leq s_2+w_2
s1+w1≤s2+w2。若能堆叠成塔,一定有
s
1
≥
w
2
s_1\geq w_2
s1≥w2或
s
2
≥
w
1
s_2\geq w_1
s2≥w1:
- 首先证明若能堆叠成塔,积木
2
2
2放下面一定是可行方案:
- 若 s 2 ≥ w 1 s_2\geq w_1 s2≥w1,已知积木 2 2 2能放在下面;
- 若 s 1 ≥ w 2 s_1\geq w_2 s1≥w2,则 w 2 + w 1 ≤ s 1 + w 1 ≤ s 2 + w 2 w_2+w_1\leq s_1+w_1\leq s_2+w_2 w2+w1≤s1+w1≤s2+w2,得 w 1 ≤ s 2 w_1\leq s_2 w1≤s2,即积木 2 2 2一定也能放在下面。得证。
- 再证明积木
2
2
2放下面一定是最优方案:
- 若积木 1 1 1放下面,两块积木作为一个整体的承重能力 s = m i n { s 2 , s 1 − w 2 } s=min\{s_2,s_1-w_2\} s=min{s2,s1−w2}。注意 s 2 + w 2 ≥ s 1 + s 2 > s 1 s_2+w_2\geq s_1+s_2>s_1 s2+w2≥s1+s2>s1,有 s 2 > s 1 − w 2 s_2>s_1-w_2 s2>s1−w2,故 s = s 1 − w 2 s=s_1-w_2 s=s1−w2。
- 若积木 2 2 2放下面,两块积木作为一个整体的承重能力 s ′ = m i n { s 1 , s 2 − w 1 } s'=min\{s_1,s_2-w_1\} s′=min{s1,s2−w1}。注意 s 1 > s 1 − s 2 s_1>s_1-s_2 s1>s1−s2且 s 2 − w 1 ≥ s 1 − w 2 s_2-w_1\geq s_1-w_2 s2−w1≥s1−w2,故恒有 s ′ ≥ s s'\geq s s′≥s。得证。
- 首先证明若能堆叠成塔,积木
2
2
2放下面一定是可行方案:
- 再证明对于
k
k
k块积木结论也成立:假设这
k
k
k块积木存在能够成塔的方案,对于方案中任意上下相邻的两块积木
i
,
j
i,j
i,j,如果
s
i
+
w
i
>
s
j
+
w
j
s_i+w_i> s_j+w_j
si+wi>sj+wj,由上面两块积木情况的结论,将它们交换顺序后,两块积木之间一定仍符合承重要求,而对外的承重能力也一定不会减少,因此
k
k
k块积木一定也仍能成塔。基于此,我们从上往下依次检查相邻的积木对,若不符合
s
i
+
w
i
≤
s
j
+
w
j
s_i+w_i\leq s_j+w_j
si+wi≤sj+wj则交换顺序。如此重复进行
k − 1 k-1 k−1轮,第 l l l轮检查范围为 1 1 1~ n − l + 1 n-l+1 n−l+1块。最终得到的堆叠方案也一定是可行的,且其作为一个整体的承重能力最强。这一过程类似于冒泡排序,最终得到的方案一定是按 s i + w i s_i+w_i si+wi递增的顺序排列的。命题得证。
动态规划
先将所有积木按 s i + w i s_i+w_i si+wi升序排序,简便起见,仍记排序后的积木为 ( s 1 , w 1 ) , ( s 2 , w 2 ) , . . . , ( s n , w n ) (s_1,w_1),(s_2,w_2),...,(s_n,w_n) (s1,w1),(s2,w2),...,(sn,wn)。
采用动态规划的思想,将“求前 i i i块积木的最大价值和 d p [ i ] dp[i] dp[i]”的问题转化为一系列求前 i − 1 i-1 i−1块积木摆放方案和最大价值和的问题。为了便于求解,为递推的变量增加“总重量为 W W W”的约束维度。则总重量为 W W W的前 i i i块积木的最大价值 d p [ i ] [ W ] dp[i][W] dp[i][W]可能由以下情况达到:
- 能够包含第 i i i块积木: d p [ i ] [ W ] = m a x { d p [ i − 1 ] [ W − w i ] + v i , d p [ i − 1 ] [ W ] } dp[i][W]=max\{dp[i-1][W-w_i]+v_i, dp[i-1][W]\} dp[i][W]=max{dp[i−1][W−wi]+vi,dp[i−1][W]}。由上述数学结论,第 i i i块积木能作为塔的一部分,当且仅当它能够作为塔的最底部,即 s i ≥ W − w i s_i\geq W-w_i si≥W−wi。
- 不能够包含第 i i i块积木, d p [ i ] [ W ] = d p [ i − 1 ] [ W ] dp[i][W]=dp[i-1][W] dp[i][W]=dp[i−1][W]。由上述数学结论,此时 s i < W − w i s_i<W-w_i si<W−wi。
综上,状态转移方程为:
d
p
[
i
]
[
W
]
=
{
m
a
x
{
d
p
[
i
−
1
]
[
W
−
w
i
]
+
v
i
,
d
p
[
i
−
1
]
[
W
]
}
,
s
i
≥
W
−
w
i
d
p
[
i
−
1
]
[
W
]
,
s
i
≥
W
−
w
i
dp[i][W]= \begin{cases} max\{dp[i-1][W-w_i]+v_i, dp[i-1][W]\},s_i\geq W-w_i\\ dp[i-1][W],s_i\geq W-w_i \end{cases}
dp[i][W]={max{dp[i−1][W−wi]+vi,dp[i−1][W]},si≥W−widp[i−1][W],si≥W−wi
每次转移的时间复杂度为 O ( 1 ) O(1) O(1)。
此外,还要设计追踪数组 t r a c k [ 1 , . . . , n ] [ ] track[1,...,n][] track[1,...,n][],记录每块积木是否入选。
伪代码
时间复杂度分析
- 排序的复杂度最小为 O ( n l o g n ) O(nlogn) O(nlogn)
- 每次转移的复杂度为 O ( 1 ) O(1) O(1),共两层循环,外层为 n n n次,而内层循环的最坏情况为 ∑ i = 1 n w i \sum\limits_{i=1}^{n}w_i i=1∑nwi次,故填表的复杂度为 O ( n ∑ i = 1 n w i ) O(n\sum\limits_{i=1}^{n}w_i) O(ni=1∑nwi)。
显然, ∑ i = 1 n w i > l o g n \sum\limits_{i=1}^{n}w_i>logn i=1∑nwi>logn,故总复杂度为 O ( n ∑ i = 1 n w i ) O(n\sum\limits_{i=1}^{n}w_i) O(ni=1∑nwi)。