BZOJ 3039:玉蟾宫 单调栈/悬线法

悬线法,刚学的

 转移方程

if(满足^&%$!@#^%){
    right[i][j]=min(right[i][j],right[i-1][j]);
    left[i][j]=max(left[i][j],left[i-1][j]);
    up[i][j]=up[i-1][j]+1;
}
int a=R[i][j]−L[i][j]+1;
int b=min(a,up[i][j]);
若要求矩形
ans=max(ans,a*up[i][j]);
若要求正方形
ans=amx(ans,b*b);

 

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e3+ 5;
int n, m;
int a[maxn][maxn];
/*
    up:up[i][j] 用于表示以(i,j)为低端的悬线的长度
    l: l[i][j] 用于表示点(i,j)左端最近的‘R'的纵坐标
    r: r[i][j] 用于表示点(i,j)右端最近的‘R'的纵坐标
    up记录上下最远的距离
    l,r记录点(i,j)左右能到达的最远的距离
*/
int up[maxn][maxn], l[maxn][maxn], r[maxn][maxn]; 
int Getchar()
{
    char ch= getchar();
    while (ch != 'F'&&ch != 'R') ch = getchar();
    return  ch == 'F';
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            a[i][j] = Getchar();
            up[i][j] = 1;
            l[i][j] = r[i][j] = j;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 2; j <= m; j++)
        {
            if (a[i][j] == 1 && a[i][j - 1] == 1)
                l[i][j] = l[i][j - 1];
        }
        for (int j = m - 1; j > 0; j--)
        {
            if (a[i][j] == 1 && a[i][j + 1] == 1)
                r[i][j] = r[i][j + 1];
        }
    }

    int max_val = -1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (i>1&&a[i][j] == 1 && a[i - 1][j] == 1)
            {
                up[i][j] += up[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]);
            }
            max_val = max(max_val, up[i][j] * (r[i][j] - l[i][j] + 1));
        }
    }
    cout << 3*max_val;
    return 0;
}
悬线法

 
单调栈

首先处理a[i][j],表示点i,j向上拓展的最大高度,然后对于每一行,就可以就按照书上的最大矩形面积处理,仔细想想,有了a数组以后,1-n行每一行的情况是不是和最大矩形面积那题类似

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 5;
int n, m, ans = -1;
/*
    a[i][j]  点(i,j)向上拓展的最大长度
*/
int a[maxn][maxn], s[maxn], w[maxn];
int Getchar()
{
    char ch = getchar();
    while (ch != 'F'&&ch != 'R') ch = getchar();
    return  ch == 'F';
}
void solve(int x)
{
    //memset(s, 0, sizeof(s));
    //memset(w, 0, sizeof(w));
    int tot = 0;
    a[x][m + 1] = 0; //小技巧 保证最后栈是空的
    for (int i = 1; i <= m + 1; i++)
    {
        if (a[x][i] >= s[tot])
        {
            s[++tot] = a[x][i];
            w[tot] = 1;
        }
        else{
            int width = 0;
            while (tot&&s[tot] > a[x][i])
            {
                width += w[tot];
                ans = max(ans, width*s[tot]);
                tot--;
            }
            s[++tot] = a[x][i];
            w[tot] = width + 1;
        }
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (Getchar()) a[i][j] = a[i - 1][j] + 1;
        }
    }
    for (int i = 1; i <= n; i++) 
        solve(i); //这里枚举每一行进行处理
    cout << 3 * ans;
    return 0;
}
```
单调栈

 

再给出一个O(nm2)的做法,这个超时了

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e3+ 5;
int n, m;
int a[maxn][maxn];
int Getchar()
{
    char ch= getchar();
    while (ch != 'F'&&ch != 'R') ch = getchar();
    return  ch == 'F';
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            a[i][j] = Getchar();
            a[i][j] += a[i][j - 1];  //列上求和
        }
    }

    int ans = -1;
    for (int i = 1; i <=m; i++)//枚举列
    {
        for (int j = i; j <= m; j++)//枚举列
        {
            int sum = 0;
            for (int k = 1; k <= n; k++) //枚举行
            {
                if (a[k][j] - a[k][i - 1] == j - i + 1) sum += j - i + 1; //满足第k行i-j列全为‘F' 
                else sum = 0;
                ans = max(ans, sum); //更新答案
            }
        }
    }
    cout << 3*ans;
    return 0;
}
O(nm^2)

 

转载于:https://www.cnblogs.com/xiaoguapi/p/10362102.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值