acm总结——悬线法(动态规划)

悬线法的作用

解决最大子矩阵问题,时间复杂度为O(nm)。

原理

对于一个n*m的矩阵,其中有若干个障碍,我们需要找到一个没有包含障碍的最大子矩阵。我们可以以一个点为起点,向上延伸出一条竖线并让其左右移动,直至移动到障碍或者矩阵的边界,进而就可以确认出一个矩阵,再遍历每一个点就能找到最大子矩阵。

操作

首先我们可以定义一个二维数组h[i][j]用于表示悬线的长度。对于每一个点,倘若他正上方的点满足题目所要求的条件(like:题目要求求没有障碍的最大子矩阵,那么满足条件便为无障碍的点),那么这一个点的竖线长度便可以加1;倘若不满足条件,便让竖线长度变为1。对此我们可以得出一个递推公式:

\large h[i][j] = \left\{\begin{matrix}h[i - 1][j] + 1 &true & \\ 1 & false & \end{matrix}\right.

现在我们已经求出了竖线的长度,对于求面积我们以求得高,我们还需要求出长,所以对于长我们可以定义两个二维数组L[i][j]、R[i][j]用于确定左右边界。

我们用L[i][j]数组表示该点能到达的最左端的位置,R[i][j]数组代表该点能到达的最右端的位置,同样也可以得到递推公式:L[i][j] = max(L[i - 1][j], L[i][j]), R[i][j] = min(R[i - 1][j], R[i][j]),那么该点的长度为R[i][j] - L[i][j]

所以对于每一个点的最大子矩阵面积为h[i][j] * (R[i][j] - L[i][j] + 1)

代码实现

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
typedef unsigned long long ull; 
#define FAST ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, n, a) for (int i = n; i >= a; i--)
#define mem(a,x) memset(a,x,sizeof(a))
#define all(x) (x).begin(), (x).end()
#define pb push_back
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define int long long
const int mod1 = 1e9 + 7, mod2 = 998244353;
const int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
const int N = 1e3 + 5, M = 2e5 + 5;
template <typename T>
inline void read(T &f){
    f = 0;T fu = 1;char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {f = (f << 3) + (f << 1) + (ch & 15); ch = getchar();}
    f *= fu;
}
int n, m;
char s[N][N];
int h[N][N];
int L[N][N], R[N][N];
signed main(){
    FAST;
    cin >> n >> m;
    rep(i, 1, n){
        rep(j, 1, m){
            cin >> s[i][j];
            L[i][j] = R[i][j] = j;
            h[i][j] = 1;
        }
    }
    rep(i, 1, n){
        rep(j, 1, m){
            if(s[i][j] == s[i][j - 1]) L[i][j] = L[i][j - 1];//针对一行时能到达最左端的位置
        }
        per(j, m, 1){
            if(s[i][j] == s[i][j + 1]) R[i][j] = R[i][j + 1];
        }
    }
    int res = 0;
    rep(i, 1, n){
        rep(j, 1, m){
            if(s[i][j] == s[i - 1][j]){
                L[i][j] = max(L[i - 1][j], L[i][j]);//加入悬线后,能到达的最最左端的位置
                R[i][j] = min(R[i - 1][j], R[i][j]);
                h[i][j] = h[i - 1][j] + 1;
            }
            //if(s[i][j])
            res = max(res, (R[i][j] - L[i][j] + 1) * h[i][j]);
        }
    }
    cout << res << endl;
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

snowwwwi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值