Processing math: 100%

Largest Submatrix 3 CodeForces - 407D (dp,好题)

大意: 给定矩阵, 求选出一个最大矩形, 满足矩形内每个元素互不相同.

 

考虑枚举上下左三个边界, 求出最大右边界的位置.

注意到固定上边界, 下边界递推时, 每个左边界对应最大右边界是单调不增的.

所以只需考虑下边界所在行的影响, 与之前的取最小即可.

set求的话复杂度是O(n3logn), 没有卡过去.

set改成vEB树的话复杂度可以达到O(n3loglogn), 或许可以过.

实际上可以发现, 下边界所在行每个点的最大右边界在上边界递减时是非增的, 可以倒序枚举上边界, 这样就可以达到复杂度O(n3).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
 
const int N = 410;
int n, m, a[N][N], R[N][N];
int l1[N], r1[N], vis[N*N];
 
 
int main() {
    scanf("%d%d", &n, &m);
    REP(i,1,n) REP(j,1,m) scanf("%d",a[i]+j);
    memset(R,0x3f,sizeof R);
    int ans = 0;
    REP(D,1,n) {
        REP(i,1,m) r1[i]=m+1,l1[i]=0;
        //l1[i] 是行范围在[U,D]内, 列在i左侧, 存在与a[D][i]相等的最接近i的列数
        //r1[i] 是行范围在[U,D]内, 列在i右侧, 存在与a[D][i]相等的最接近i的列数
        //R[U][i] 为上边界U, 下边界D, 左边界i的矩形的最大右边界
        PER(U,1,D) {
            int now = 1;
            REP(i,1,m) {
                now = max(now, i);
                while (now<r1[i]&&!vis[a[U][now]]&&!vis[a[D][now]]) {
                    if (U!=D&&a[U][now]==a[D][now]) break;
                    vis[a[U][now]] = vis[a[D][now]] = 1;
                    ++now;
                }
                vis[a[U][i]] = vis[a[D][i]] = 0;
                r1[i] = min(r1[i], now);
            }
            now = m;
            PER(i,1,m) {
                now = min(now, i);
                while (now>l1[i]&&!vis[a[U][now]]&&!vis[a[D][now]]) {
                    if (U!=D&&a[U][now]==a[D][now]) break;
                    vis[a[U][now]] = vis[a[D][now]] = 1;
                    --now;
                }
                vis[a[U][i]] = vis[a[D][i]] = 0;
                l1[i] = max(l1[i], now);
            }
            REP(i,1,m) {
                R[U][i] = min(R[U][i],r1[i]-1);
                R[U][l1[i]] = min(R[U][l1[i]],i-1);
            }
            PER(i,1,m) {
                R[U][i] = min(R[U][i], R[U][i+1]);
                ans = max(ans, (D-U+1)*(R[U][i]-i+1));
            }
        }
    }
    printf("%d\n", ans);
}

 

还有一种编码非常简单的区间DP做法.

fi,l,r为下边界i,左右边界l,r的最小上边界值, 有转移

fi,l,r=max{fi1,l,r,fi,l+1,r,fi,l,r1,ai,la1..i,r的限制,a1..i,lai,r的限制}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <algorithm>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
 
const int N = 402;
int n,m,a[N][N],f[N][N],pre[N][N*N];
 
int main() {
    scanf("%d%d", &n, &m);
    REP(i,1,n) REP(j,1,m) scanf("%d",a[i]+j);
    int ans = 0;
    REP(i,1,n) REP(d,1,m) {
        for (int l=1,r=d; r<=m; ++l,++r) {
            if (l==r) f[l][l]=max(f[l][l],pre[l][a[i][l]]);
            else if (a[i][l]==a[i][r]) f[l][r] = i;
            else {
                f[l][r]=max({f[l][r],f[l][r-1],f[l+1][r],pre[r][a[i][l]],pre[l][a[i][r]]});
            }
            ans = max(ans, (i-f[l][r])*(r-l+1));
        }
        REP(j,1,m) pre[j][a[i][j]]=i;
    }
    printf("%d\n", ans);
}

 

 

 

 

 

 

posted @   uid001  阅读(310)  评论(0)    收藏  举报
(评论功能已被禁用)
编辑推荐:
· MySQL下200GB大表备份,利用传输表空间解决停服发版表备份问题
· 记一次 .NET某固高运动卡测试 卡慢分析
· 微服务架构学习与思考:微服务拆分的原则
· 记一次 .NET某云HIS系统 CPU爆高分析
· 如果单表数据量大,只能考虑分库分表吗?
阅读排行:
· 7 个最近很火的开源项目「GitHub 热点速览」
· DeepSeekV3:写代码很强了
· 记一次 .NET某固高运动卡测试 卡慢分析
· Visual Studio 2022 v17.13新版发布:强化稳定性和安全,助力 .NET 开发提
· MySQL下200GB大表备份,利用传输表空间解决停服发版表备份问题
点击右上角即可分享
微信分享提示