[BZOJ 3039] 玉蟾宫

题面:http://www.lydsy.com/JudgeOnline/problem.php?id=3039

 

归根到底,这是一个求最大子矩阵的经典问题

下面是一篇国家队论文,对这类问题进行了系统的分析:

浅谈用极大化思想解决最大子矩阵问题:https://wenku.baidu.com/view/bc8311f69e314332396893f7.html?re=view###

 

首先,最大子矩阵必然是一个极大的子矩阵,也就是说4条边上必然有不能包含的点。

这样,对于不同情况下的这个问题,有两种解决方案:

A:当点数较小时,使用从左向右以点为线索扫描,复杂度O(S^2)

B:当点数较密集但方格数较少时,以每一个节点为线索进行DP,使用悬线法+极大化思想,复杂度O(NM)

 

我已开始并不了解悬线法,但想出了与之相近的思路:在每一层上时只考虑此层及以上的情况,单调栈维护向左和向右能到达的最远距离。不过由于未使用DP,效率上可能略微有些差异。

#include <bits/stdc++.h>

using namespace std;
int n,m,pre[1005][1005];

int main()
{
    cin >> n >> m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            char ch;cin >> ch;
            if(ch=='F') pre[i][j]=pre[i][j-1]+1;
            else pre[i][j]=0;
        }
    
    int res=0;
    for(int k=1;k<=m;k++)
    {
        int l[1005],r[1005];
        memset(l,0,sizeof(l));memset(r,0,sizeof(r));
        stack<int> s;
        
        l[1]=1;s.push(1);
        for(int i=2;i<=n;i++)
        {
            while(!s.empty() && pre[i][k]<=pre[s.top()][k]) s.pop();
            if(s.empty()) l[i]=1;
            else l[i]=s.top()+1;
            s.push(i);
        }
        
        while(!s.empty()) s.pop();
        
        r[n]=n;s.push(n);
        for(int i=n-1;i>=1;i--)
        {
            while(!s.empty() && pre[i][k]<=pre[s.top()][k]) s.pop();
            if(s.empty()) r[i]=n;
            else r[i]=s.top()-1;
            s.push(i);
        }
        
        int sum=0;
        for(int i=1;i<=n;i++) sum=max(sum,pre[i][k]*(r[i]-l[i]+1));
        res=max(res,sum);
    }
    cout << 3*res;
    return 0;
}

 

Review:

1、这道题中首先可以总结的就是极大化思想,本质上是一种基础的贪心

2、在一个线性序列中求解含有限制条件的最值或区间类问题时,可以更多考虑用单调栈或单调队列进行维护

3、在面对不同数据范围时,尽量选取范围小的参数作为线索进行算法构造

posted @ 2017-12-07 23:04  NewErA  阅读(324)  评论(0编辑  收藏  举报