分类
从$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$
数组$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$
有面值为 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[i]代表硬币的面额
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)
}
}
}
无向图 G 有个 n 结点及一些带有正的权重值边, 找到结点 1 到 n 的最短路径
- 原问题
找到节点 1 到节点 n 的最短路径
- 中间状态
$dp[i]$表示1 到节点 i 的最短路径, 最终求 $dp[n]$
- 边界值
$dp[1] = 0$
- 状态转移方程
$dp[i] = min(dp[i], dp[j]+dis[i][j])$, $dis[i][j]$ 代表 $j$ 到 $i$ 的距离
const int 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])
}
}
}
示例
最长上升子序列
原问题
前 $n$ 个数中最长上升子序列
子问题
前 $1$ 个数的最长上升子序列…前 $n$ 个数的最长上升子序列
中间状态
$dp[i]$代表前 $i$ 范围内的中的最长上升子序列, 最终求 $dp[n]$
边界值
$dp[1] = 1$
状态转移方程
-
状态 $i = 2$, $dp[2] = max(dp[1], dp[1] + 1)$
-
状态 $i$, $dp[i] = max(dp[i], dp[j] + 1)$, $j < i$ 且 $a[j] < a[i]$
memset(dp, 1, sizeof(dp));
for (int i = 2; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (a[j] < a[i]) {
dp[i] = dp[i] > (dp[j] + 1) ? dp[i] : (dp[j] + 1);
}
}
}
路径获取值
平面上有 $n*m$ 个格子, 每个格子中放一定数量的苹果
从左上角开始, 每一步只能向下或向右走, 走到一个格子获得格子内苹果, 走到右下角最多能收集到多少苹果
中间状态
$a[i][j]$代表i行j列格子里放的苹果数
$dp[i][j]$代表走到i行j列格子时获得的总苹果数
边界值
- 若在第0行, 则只能从左边过来
$dp[0][j] = dp[0][j - 1] + a[0][j]$
graph LR;
A(Start) --> B(2) --> C(3) --> D(4)
- 若在第0列, 则只能从上边过来
$dp[i][0] = dp[i - 1][0] + a[i][0]$
graph TB;
A(Start) --> B(2) --> C(3) --> D(4)
状态转移方程
- $dp[i][j] = max(dp[i - 1][j]+ dp[i][j - 1]) + a[i][j]$
dp[0][0] = a[0][0];
for(int i = 0; i <= n; ++i) {
for(int j = 0; j <= m; ++j){
if(i == 0 && j == 0){
dp[0][0] = a[0][0];
}
else if(i == 0){
dp[0][j] = dp[0][j - 1] + a[0][j]
}
else if(j == 0){
dp[i][0] = dp[i - 1][0] + a[i][0]
}
else{
dp[i][j] = max(dp[i - 1][j] + dp[i][j - 1]) + a[i][j]
}
}
}