动态规划(DP)(一)

动态规划(DP)(一)

核心:状态转移方程
定义:.
动态规划是分阶段求最优值的算法。
将复杂问题按阶段划分成子问题;
枚举子问题各种可能情况,从中找出最优值;
利用子问题的最优值求得源问题的最优解。

动态规划是分阶段求最优值的算法。将复杂问题按阶段划分成子问题;枚举子问题各种可能情况,从中找出最优值;利用子问题的最优值求得源问题的最优解

动态规划问题具有以下基本特征:
问题具有多阶段决策的特征。 每一阶段都有相应的“状态”与之对应,描述状态的量称为“状态变量”。每一阶段都面临一个决策,选择不同的决策将会导致下一阶段不同的状态。每一阶段的最优解问题可以递归地归结为下一阶段各个可能状态的最优解问题,各子问题与原问题具有完全相同的结构。

动态规划的几个概念:
阶段:据空间顺序或时间顺序对问题的求解划分阶段。状态:描述事物的性质,不同事物有不同的性质,因而用不同的状态来刻画。对问题的求解状态的描述是分阶段的。决策:根据题意要求,对每个阶段所做出的某种选择性操作。状态转移方程:用数学公式描述与阶段相关的状态间的演变规律。

动态规划的几个步骤:
1、判断问题是否具有最优子结构性质,若不具备则不能用动态规划。
2、把问题分成若干个子问题(分阶段)。
3、建立状态转移方程(递推公式)。
4、找出边界条件。
5、将已知边界值带入方程。
6、递推求解。

1.吃金币问题(最简单的动态规划)
如果用F[a][b]表示达到(a,b)这个点时所能吃到的最大金币数量。
得到递推关系式:
F[a][b]=max(F[a-1][b],F[a][b-1])+Coin[a][b]
(此步即为递归定义最优解的值,列出状态转移方程)
F[m][n]即为所求。

2.最长上升子序列
如何把这个问题分解成子问题呢?经过分析,发现 “求以ak(k=1, 2, 3…N)为终点的最长上升子序列的长度”是个好的子问题。
序列中数的位置k 就是“状态”,而状态 k 对应的“值”,就是以ak 做为“终点”的最长上升子序列的长度。这个问题的状态一共有N 个。
假定MaxLen (k)表示以ak 做为“终点”的最长上升子序列的长度,那么:
MaxLen (1) = 1
MaxLen (k) = Max { MaxLen (i):1<i < k 且 ai < ak 且 k≠1 } + 1

3.最长公共子序列
DP[i][j]代表 A串从起点到i位置的字串 和 B串从起点到j位置的字串 的LCS
A[i] == B[j] 时 :DP[i][j] = DP[i-1][j-1] + 1
A[i] != B[j] 时 : DP[i][j] = MAX(DP[i-1][j] , DP[i][j-1])

4.最大子段和问题
状态设计:
dp[i] (1 <= i <= N) 表示以 a[i] 结尾的最大连续子段和。
显然,dp[i] >= 0 (1 <= i <= N)
求解目标:
max{dp[i]} (1 <= i <= N)
状态转移方程:
dp[i] = max{a[i],0} (i = 1)
dp[i] = max{dp[i-1] + a[i], 0} (2 <= i <= N)

5.最大子矩阵和
一般的做法依然是枚举:枚举子矩阵最上和最下的行以及最左和最右的列,但仅是这样就已经是 O(N^4) 的复杂度了。对于 N <= 100 以及 1s 的时限来说,这样的做法是不能够接受的。
如何根据子矩阵的特性来解决此题?
正确解法:每次枚举子矩阵最上的行 u 和最下的行 d,再把这个子矩阵每一列的值相加,压缩成一个一维的数组,对这个数组求其最大子段和,这样就相当于把所有最上的行为 u 、最下的行为 d 的最大子矩阵和求出来了。
算法效率:
时间复杂度:O(N^3)
空间复杂度:O(N^2)

6.花束摆放问题
设A(i,j)表示第i种花束摆在第j个花瓶中获得的美学值。 S[i,k]表示第i种花束摆在第k个之前(包括第k个)的任意某个花瓶中,前i种花束能够获得的最大美学值(之和)。这样,原问题的最优值即为S[F,V]。
其递归方程为:
S[i,k]=max{S[i-1,k-1]+A(i,k),S[i,k-1]},(i>1,k>i);
初始条件为:
S[1,1]=A[1,1]; S[1,k]=max{A(1,k),S[1,k-1]},(k>1);
S[i,i]=S[i-1,i-1]+A(i,i), (i>1)

7.复制书稿
设dp[i][j]表示前i本书由j个人复制所需要的最少时间,
有状态转移方程
dp[i][j]=min(dp[i][j],max(dp[v][j-1],sum[v+1][i]))
其中1<=i<=m,1<=j<=k,j-1<=v<=i-1,
sum[v+1][j]表示第v+1本书到第i本书的页数之和。

8.最大M段子段和问题
问题分析:
令 dp[i][j] 表示在前i个数中选取j段且第i个数在最后一组中的最大子段和
那么现在对于第i个数有两种决策
第i个数和第i-1个数连接成一段
dp[i][j] = dp[i-1][j] + a[i]
第i个数自己单独做一段.那么前面就需要有 j-1段
dp[i][j] = max{dp[k][j-1]|j-1<=k<i}+a[i]
就有了状态转移方程
dp[i][j]=max(dp[i-1][j],max{dp[k][j-1]|j - 1<= k < i})+a[i];

上面这个时间复杂度0(nnm) 空间复杂度O(n*n) n高达1000000 简直可怕再来看看转移方程:dp[i][j]=max(dp[i-1][j],max{dp[k][j-1]|j-1<=k<i})+a[i];发现更新 dp[i][j]的时候只用到了dp[.][j]和 dp[.][j-1]里面的值。也就是dp[.][0~j-2]都已经没用了j只用开两列就够了,也就是所谓的滚动数组了 用 dp[.][1]和dp[.][0]来轮换表示dp[.][j]和dp[.][j-1]。这样空间复杂度就降到了O(n) 这是可以接受的

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值