【题解】最大矩阵区间
题意简述
给定一个 \(n\) 行 \(m\) 列的矩阵 ,矩阵 \(A\) 中只包含整数。
对于每一行 \(i\),选择一个非空区间 \(l_i,r_i\);使得相邻行的区间相交,并最大化所有区间内整数的总和。具体来说,选择 \(l_i,r_i\) 使得 \(1 \le l \le r \le m\),对于所有 \(i \in Z ∩ [1,n)\) ,满足 \(\max(l_i,l_{i+1}) \le min(r_i,r_{i+1})\),并且最大化 \(\sum_{i=1}^{n} \sum_{j=l_i}^{r_i}A_{i,j}\)。
题目分析
这个数据范围正解肯定不是区间 DP。
我们设计区间 DP 是因为要保证上下区间有交,那么既然要有交只需要满足有一个点交即可。
那么令 \(dp_{i,j}\) 表示前到了第 \(i\) 行,该行覆盖了 \(j\) 的区间的最大价值。
\(n \times m \le 10^6\),空间滚一下。这里令 \(f\) 为 \(dp_{pre}\),\(g\) 为 \(dp_{now}\)。
那么对于一个点 \(j\),分类讨论。
与上一行交在左边:
- 右边:以 \(j\) 为右端点的最大连续和,记为 \(pre_{j,0}\)。
- 左边:令 \(pre_{j,1}\) 表示自 \(j\) 向左连续的一段结果且保证其中会与上一行有交。可以是左边有交,多选一个 \(a[j]\),也可以是在 \(j\) 交,加上以 \(j\) 为右端点的最大连续和。故转移为:\(pre_{j,1} = \max(pre_{j-1,1},f_{j}+pre_{j-1,0})+a[j]\)。
右边同理,处理出 \(suf\)。
那么对于点 \(j\),答案就是 \(\max(pre_{j-1,0} + suf_{j,1},pre_{j,1} + suf_{j+1,0})\)。
CODE
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10,inf = 0x3f3f3f3f3f3f3f3f;
int dp[2][N],*f,*g,n,m;
int a[N],pre[N][2],suf[N][2],ans = -inf;
signed main(){
scanf("%lld%lld",&n,&m);
pre[0][1] = suf[m+1][1] = -inf;
for(int i = 1;i<=n;++i){
g = dp[(i&1)], f = dp[((i-1)&1)];
for(int j = 1;j<=m;++j)scanf("%lld",&a[j]);
for(int j = 1;j<=m;++j){
pre[j][0] = max(pre[j-1][0]+a[j],0ll);
pre[j][1] = max(pre[j-1][1],pre[j-1][0]+f[j])+a[j];
}
for(int j = m;j;--j){
suf[j][0] = max(suf[j+1][0]+a[j],0ll);
suf[j][1] = max(suf[j+1][1],suf[j+1][0]+f[j])+a[j];
}
for(int j = 1;j<=m;++j)g[j] = max(pre[j-1][0]+suf[j][1],suf[j+1][0]+pre[j][1]);
}
for(int i = 1;i<=m;++i)ans = max(ans,g[i]);
printf("%lld",ans);
return 0;
}
反思:
把握关键信息,将无效或冗余的信息代替。我们这里区间 DP 的 \(l,r\) 是冗余的,因为我们记这个只是为了保证上下区间有交。而有交可以用一个位置保证,故记录状态为"覆盖了 \(j\) 的区间的最大价值"。

浙公网安备 33010602011771号