【学习笔记】线性动态规划1
John_yangliwu · · 个人记录
最大子段和
问题引入
给定整数序列
思路
首先,一个子段
于是考虑枚举
从
设计一种动态规划,设
要么接上以
进一步简化,对于每个
例题 1 :P1719 最大加权矩形
思路
对每行求前缀和。
枚举左右边界
核心代码
void calc(int l, int r) {
int s = 0;
for(int i = l; i <= n; i++) {
s += sum[i][r] - sum[i][l - 1];
if(sum < 0) sum = 0;
res = max(s, res);
}
}
例题 2 :P1121 环状最大两段子段和
思路
分成两个子问题:
- 求一个非环状序列的最大两段子段和。
- 求一个环状序列的最大一段子段和。
首先看第一个。
设
用两次对
最终答案是
对于第二个问题,我们可以思考最优解“长什么样子”。
有两种情况(用
-
\texttt{xxxoooooxxx} -
\texttt{oooxxxxxooo}
第一种情况直接用最大子段和求解,第二种则是序列总和减去最小子段和,分别求取最大值即可。
然后,把两种情况结合起来,原题的最优解也有两种“样子”:
-
\texttt{xxxooxxooxxx} -
\texttt{oooxxooxxooo}
第一种便是子问题
两种情况类似,都可以用子问题
核心代码
int calc() {
memset(dp, 0, sizeof(dp));
dp[0][0] = dp[0][1] = dp[n + 1][0] = dp[n + 1][1] = -2e9;
for(int i = 1; i <= n; i++) {
if(dp[i - 1][0] > 0)
dp[i][0] += dp[i - 1][0];
dp[i][0] += a[i];
}
for(int i = n; i >= 1; i--) {
if(dp[i + 1][1] > 0)
dp[i][1] += dp[i + 1][1];
dp[i][1] += a[i];
}
for(int i = 1; i <= n; i++)
dp[i][0] = max(dp[i][0], dp[i - 1][0]);
for(int i = n; i >= 1; i--)
dp[i][1] = max(dp[i][1], dp[i + 1][1]);
int ans = -1e9;
for(int i = 1; i <= n; i++)
ans = max(ans, dp[i][0] + dp[i + 1][1]);
return ans;
}
最长上升子序列
问题引入
给出一个长度为
思路
首先子序列不是子段。
设
其中
仔细观察发现,每次决策,肯定是选择
所以维护一个数组
对于每个
因为