动规经典问题
1.递推求解
- 斐波那契数列 F ( n ) = F ( n − 1 ) + F ( n − 2 ) ( n > 2 ) ; F ( 1 ) = F ( 2 ) = 1 F(n) = F(n-1)+F(n-2)(n>2) ;F(1)=F(2)=1 F(n)=F(n−1)+F(n−2)(n>2);F(1)=F(2)=1
- 台阶公式 F ( n ) = F ( n + 1 ) + F ( n + 2 ) ( n > 2 ) ; F ( 1 ) = 1 ; F ( 2 ) = 2 F(n)=F(n+1)+F(n+2)(n>2) ;F(1)=1;F(2)=2 F(n)=F(n+1)+F(n+2)(n>2);F(1)=1;F(2)=2
- 排错公式 F ( n ) = ( n − 1 ) F ( n − 1 ) + ( n − 2 ) F ( n − 2 ) ( n > 2 ) ; F ( 1 ) = 0 ; F ( 1 ) = 1 F(n)=(n-1)F(n-1)+(n-2)F(n-2)(n>2);F(1)=0;F(1)=1 F(n)=(n−1)F(n−1)+(n−2)F(n−2)(n>2);F(1)=0;F(1)=1
补充:
N
N
N阶楼梯上楼问题:每次上楼可以上一阶楼梯或两阶楼梯,
N
N
N阶楼梯可以有多少种方式?
首先
F
(
1
)
=
1
F(1)=1
F(1)=1,
F
(
2
)
=
2
F(2)=2
F(2)=2是可以直接得到的,那么当
n
n
n大于
2
2
2时,我考虑每种上台阶方式的最后一步,由于只有两种行走方法,因此它只可能是从
n
−
1
n-1
n−1阶经过一步走到
n
n
n阶,或者从
n
−
2
n-2
n−2阶经过两步走到
n
n
n阶。因此
F
(
n
)
=
F
(
n
−
1
)
+
F
(
n
−
2
)
F(n)=F(n-1)+F(n-2)
F(n)=F(n−1)+F(n−2)。
排错问题:
n
n
n个信封,问如果所有的信都装错信封,一共有多少种装错方式?
假设有
n
n
n封信,第一封信可放在(
2
2
2~
n
n
n)的任一个信封里,共
n
−
1
n-1
n−1种放法,设第一封信放在了第
k
k
k个信封里,若此时第
k
k
k封信放在了第
1
1
1个信封里,则只要将剩下的
n
−
2
n-2
n−2错排,即
F
(
n
−
2
)
F(n-2)
F(n−2),若第
k
k
k封信没有放在了第
1
1
1个信封里,可将第
1
1
1封信的位置看成是“第
k
k
k个位置”,即将
n
−
1
n-1
n−1封信错排,即为
F
(
n
−
1
)
F(n-1)
F(n−1),因此
F
(
n
)
=
(
n
−
1
)
F
(
n
−
1
)
+
(
n
−
2
)
F
(
n
−
2
)
(
n
>
2
)
;
F
(
1
)
=
0
;
F
(
1
)
=
1
F(n)=(n-1)F(n-1)+(n-2)F(n-2)(n>2);F(1)=0;F(1)=1
F(n)=(n−1)F(n−1)+(n−2)F(n−2)(n>2);F(1)=0;F(1)=1。
2.最长递增子序列
已知序列 { a 1 , a 2 , . . . , a n } \{a_{1},a_{2},...,a_{n}\} {a1,a2,...,an}中,取出若干数组成新的序列 { a i 1 , a i 2 , . . . , a i m } \{a_{i1},a_{i2},...,a_{im}\} {ai1,ai2,...,aim},其中下标 i 1 i1 i1, i 2 i2 i2,…, i m im im保持递增,即新数列中各个数之间依旧保持原数列中的先后顺序,那么称 { a i 1 , a i 2 , . . . , a i m } \{a_{i1},a_{i2},...,a_{im}\} {ai1,ai2,...,aim}为原序列的一个子序列,如果在子序列中,当下标 i x > i y ix>iy ix>iy且 a i x > a i y a_{ix}>a_{iy} aix>aiy,那么称这个子序列为原序列的一个递增子序列。
设 F ( i ) F(i) F(i)表示序列中以 a i a_{i} ai为末元素的最长递增子序列的长度。则有如下的递推方程:
这个递推方程的意思是,在求以 a i a_{i} ai为末元素的最长递增子序列时,找 a j a_{j} aj, j < i j<i j<i且 a j < a i a_{j}<a_{i} aj<ai。如果这样的元素存在,那么对所有 a j a_{j} aj,都有一个以 a j a_{j} aj为末元素的最长递增子序列的长度 F ( j ) F(j) F(j),把其中最大的 F ( j ) F(j) F(j)选出来,那么 F ( i ) F(i) F(i)就等于最大的 F ( j ) F(j) F(j)加上 1 1 1,即以 a i a_{i} ai为末元素的最长递增子序列,等于以使 F ( j ) F(j) F(j)最大的那个 a j a_{j} aj为末元素的递增子序列最末再加上 a i a_{i} ai;如果这样的元素不存在,那么 a i a_{i} ai自身构成一个长度为 1 1 1的以 a i a_{i} ai为末元素的递增子序列。
for(int i=0;i<n;i++)
{
int imax=1;
for(int j=0;j<i;j++)
{
if(a[j]<a[i])
imax = max(imax,f(j)+1)
}
f[i]=imax;
}
3.最长公共子序列(LCS)
有两个字符串 S 1 S 2 S_{1}S_{2} S1S2,求第三个字符串 S 3 S_{3} S3,它同时为 S 1 S 2 S_{1}S_{2} S1S2的子串,且要求它的长度最长。我们用 d p [ i ] [ j ] dp[i][j] dp[i][j]来表示 S 1 S_{1} S1中前 i i i个字符与 S 2 S_{2} S2中前 j j j个字符的最长公共子串的长度。
当
S
1
[
i
]
=
S
2
[
j
]
S_{1}[i]=S_{2}[j]
S1[i]=S2[j],
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
−
1
]
+
1
dp[i][j]=dp[i-1][j-1]+1
dp[i][j]=dp[i−1][j−1]+1;
当
S
1
[
i
]
!
=
S
2
[
j
]
S_{1}[i]!=S_{2}[j]
S1[i]!=S2[j],
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为
S
1
S_{1}
S1中前
i
−
1
i-1
i−1个字符与
S
2
S_{2}
S2中前
j
j
j个字符的最长公共子串 和
S
1
S_{1}
S1中前
i
i
i个字符与
S
2
S_{2}
S2中前
j
−
1
j-1
j−1个字符的最长公共子串 的较大值。
显然,当
i
=
0
i=0
i=0或
j
=
0
j=0
j=0时,即
d
p
[
0
]
[
j
]
=
0
dp[0][j]=0
dp[0][j]=0,
d
p
[
i
]
[
0
]
=
0
dp[i][0]=0
dp[i][0]=0。
那么 d p [ i ] [ j ] dp[i][j] dp[i][j]的地推公式:
d p [ i ] [ j ] { d p [ i − 1 ] [ j − 1 ] + 1 S 1 [ i ] = S 2 [ j ] m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) S 1 [ i ] ! = S 2 [ j ] d p [ 0 ] [ j ] = d p [ i ] [ 0 ] = 0 0 ≤ i ≤ n , 0 ≤ j ≤ m dp[i][j]\left\{\begin{matrix} dp[i-1][j-1]+1 & S_{1}[i]=S_{2}[j]\\ max(dp[i-1][j],dp[i][j-1]) &S_{1}[i]!=S_{2}[j] \\ dp[0][j]=dp[i][0]=0 &0\leq i\leq n,0\leq j\leq m \end{matrix}\right. dp[i][j]⎩⎨⎧dp[i−1][j−1]+1max(dp[i−1][j],dp[i][j−1])dp[0][j]=dp[i][0]=0S1[i]=S2[j]S1[i]!=S2[j]0≤i≤n,0≤j≤m
4.0-1背包问题
0 − 1 0-1 0−1背包:有一个容量为 V V V的背包和一些物品,这些物品分别有两个属性,体积 v v v和价值 w w w,每个物品只有两个状态,取或不取。要求用这个背包装下价值尽可能多的物品,求该最大价值,背包可以不被装满。
我们用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示背包总体积不超过j的情况下,前i个物品所能达到的最大价值。
初始时,
d
p
[
0
]
[
j
]
=
0
dp[0][j]=0
dp[0][j]=0。若物品
i
i
i放入背包,设其体积为
w
w
w,价值为
v
v
v,则
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]=
d
p
[
i
−
1
]
[
j
−
w
]
+
v
dp[i-1][j-w]+v
dp[i−1][j−w]+v,即在总体积不超过
j
−
w
j-w
j−w时前
i
−
1
i-1
i−1件物品可组成的最大价值的基础上再加上
i
i
i物品的价值
v
v
v;若物品不加入背包,则
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
dp[i][j]=dp[i-1][j]
dp[i][j]=dp[i−1][j],即此时与总体积不超过
j
j
j的前
i
−
1
i-1
i−1件物品组成的价值最大值等价。选择他们之中较大值成为状态
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]的值。综上,
0
−
1
0-1
0−1背包的状态转移方程为:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
−
w
]
+
v
,
d
p
[
i
−
1
]
[
j
]
)
dp[i][j]=max(dp[i-1][j-w]+v,dp[i-1][j])
dp[i][j]=max(dp[i−1][j−w]+v,dp[i−1][j])
更通俗点讲就是:
(1)当w[i] > j的时候,说明放不下去,所以肯定不能放i号物品,所以:
m[i][j] = m[i-1][j]
(2)当w[i] <= j的时候,说明放的下去,那么问题就是放不放,如果放的话价值是m[i-1][j-w[i]] + v[i];如果不放下去,就是m[i][j] = m[i-1][j]。所以放不放显然取决于两个值的大小。
if(j>=w[i])
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
else
dp[i][j]=dp[i-1][j];