动态规划思路
- 原问题 子问题
- 中间状态
- 边界值
状态转移方程
例1
数组最优解
从n个数的数组,取出若干个数两两不相邻的数,求取出的数最大和
-
原问题 : n个数取出和最大的最优解
-
子问题 : 前1个数的最优解,前2个数的最优解…前n个数的最优解…
-
中间状态 : dp[i]代表前i个数的最优解,最终需求dp[n]
-
边界值 : dp[1] = a[1] dp[2] = max(dp[1] ,a[2])
-
状态转移方程 : 前i个数的最优解,重点是考虑加不加第i个数a[i];若不加可从dp[i-1]转移过来,若加只能从dp[i-2]转移过来(选择的数不能相邻)
然后再取两种情况的最大值.因此:dp[i] = max( dp[i-1], dp[i-2]+a[i] ) {i>=2}
例2
连续数组最大和
在一个数组中a[n]有正数有负数,求取出的连续子数组中最大和
-
原问题 : 前n个数中连续子数组最大和
-
子问题 : 前1个数的最大和,前2个数的最大和…前n个数的最大和…
-
中间状态 : dp[i]代表前i个数的最大和,最终需求dp[n]
-
边界值 : dp[1] = a[1] dp[2] = max(dp[2] ,dp[2]+a[1])
-
状态转移方程 : 对于状态i来说,若前一状态dp[i-1]为负数,则不需要前面的数,新状态为a[i];若dp[i-1]是正数,那就可以加上a[i]。关键就在于前面的状态还要不要加上
dp[i]=max( a[i], dp[i-1]+a[i]) {i>=1}
例3
一个数组a[n],求出最长上升子序列的长度
-
原问题 : 前n个数中最长上升子序列
-
子问题 : 前1个数的最长上升子序列…前n个数的最长上升子序列…
-
中间状态 : dp[i]代表前i个数的最长上升子序列,最终需求dp[n]
-
边界值 : dp[1] = 1
-
状态转移方程 : i = 2时,dp[2] = max(dp[1],dp[1]+1),即判断a[1]大于或小于a[2]的情况;对于状态i来说 dp[i] = max(dp[i],dp[j]+1) {j< i 并且 a[j] < a[i]}
for(int i = 2 ; i <= n ; i++){
dp[i] = 1;
for(int j = 1 ; j < i ; j++){
if(a[j] < a[i]){
dp[i] = max(dp[i],dp[j]+1);
}
}
}
例4
有面值为1,3,5的硬币若干枚,如何用最少的硬币凑够n元
-
原问题 : 凑够n元所需最少硬币
-
中间状态 : dp[i]代表凑够i元所需最少硬币,最终需求dp[n]
-
边界值 : dp[1] = 1
-
状态转移方程 : dp[i] = min(dp[i-1],dp[i-3],dp[i-5]) + 1
v[1] = 1,v[2] = 3,v[3] = 5;
for(int i = 1 ; i <= n ; i++){
for(int j = 3 ; j >= 1 j--){
if(i>v[j]){
dp[i] = min(dp[i],dp[i-v[j]]+1)
}
}
}
例5
无向图G有个n结点及一些边,每条边带有正的权重值。找到结点1到结点n的最短路径
-
原问题 : 找到节点1到节点n的最短路径
-
中间状态 : dp[i]到节点i的最短路径,最终需求dp[n]
-
边界值 : dp[1] = 0
-
状态转移方程 : dp[i] = min(dp[i] , dp[j]+dis[i][j]) {dis[i][j] 代表j到i的距离}
MAX = 0x7fffffff;
dp[1] = 0;
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j < i ; j++){
//若i能到j
if(dis[i][j] < MAX){
dp[i] = min(dp[i] , dp[j]+dis[i][j])
}
}
}
例6
平面上有n*m个格子,每个格子中放一定数量的苹果a[i][j],从左上角开始,每一步只能向下或向右走,走到一个格子获得格子内苹果,走到右下角最多能收集到多少个苹果
- 中间状态 : dp[i][j]代表走到[i,j]时获得的苹果数
- 边界值dp[i][1] = dp[i-1][1]+a[i][1] ; dp[1][j] = dp[i][j-1] +a[1][j]
- 状态转移方程 : dp[i][j] = max(dp[i-1][j]+ dp[i][j-1])+a[i][j]
dp[1][1] = a[1][1]
for(int i = 1 ; i <=n ; i++){
for(int j = 1 ; j <=n ; j++){
if(i == 1){
dp[i][j] = dp[i][j-1] + a[i][j]
}
else if(j == 1){
dp[i][j] = dp[i-1][j] + a[i][j]
}
else{
dp[i][j] = max(dp[i-1][j]+ dp[i][j-1])+a[i][j]
}
}
}