【题解】最大矩阵区间

题意简述

给定一个 \(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\) 的区间的最大价值"。

posted @ 2024-08-26 16:45  Luzexxi  阅读(29)  评论(0)    收藏  举报