[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、在面对不同数据范围时,尽量选取范围小的参数作为线索进行算法构造