• 有关最大子矩阵的说明


    很久以前,我做过一些最大子矩阵的问题,当时用的是悬线法,这是一个有意思的算法,举例:

    棋盘制作

    (码风稍微有些奇怪,毕竟这是很久以前做的了,忍忍吧)

    悬线法的重点在于处理出将每一个点可以向两边扩展的最远位置,再从上到下求出每个点可以向上扩展的位置。

    算法复杂度为(O_{nm}),常数有点大。

    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    int a[1001][1001],l[1001][1001],r[1001][1001],h[1001][1001];
    int main(){
        cin>>n>>m;
        for(int i=1;i<=n;i++)
          for(int j=1;j<=m;j++)
          {
          	cin>>a[i][j];
          	l[i][j]=r[i][j]=j;
          	h[i][j]=1;
          }
        for(int i=1;i<=n;i++)
          for(int j=2;j<=m;j++)
            if(a[i][j]!=a[i][j-1])
              l[i][j]=l[i][j-1];
        for(int i=1;i<=n;i++)
          for(int j=m-1;j>=1;j--)
            if(a[i][j]!=a[i][j+1])
              r[i][j]=r[i][j+1];
        int ans1=0,ans2=0;
        for(int i=1;i<=n;i++)
          for(int j=1;j<=m;j++)
          {
          	if(i!=1&&a[i][j]!=a[i-1][j])
          	{
          		l[i][j]=max(l[i][j],l[i-1][j]);
                r[i][j]=min(r[i][j],r[i-1][j]);
                h[i][j]=h[i-1][j]+1;
            }
            int a=r[i][j]-l[i][j]+1;
            int b=min(a,h[i][j]);
            ans1=max(ans1,b*b);
            ans2=max(ans2,a*h[i][j]);
          }
        cout<<ans1<<endl<<ans2;
    }
    

    然而今天在做到(HDU2870)时,很显然我写了个悬线法,光荣的获得了(TLE)……

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    using namespace std;
    const int N=1010;
    int n,m,ans,num[N][N];
    char las[N][N],now[N][N];
    int l[N][N][3],r[N][N][3],h[N][N][3];
    inline int max(int a,int b) {return a>b?a:b;}
    inline void Change(int p)
    {
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=m;j++)
                if(p==0&&(las[i][j]=='w'||las[i][j]=='y'||las[i][j]=='z')) now[i][j]='a';
                else if(p==1&&(las[i][j]=='w'||las[i][j]=='x'||las[i][j]=='z')) now[i][j]='b';
                else if(p==2&&(las[i][j]=='x'||las[i][j]=='y'||las[i][j]=='z')) now[i][j]='c';
        memset(num,0,sizeof(num));
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=m;j++)
                if(now[i][j]=='a'&&p==0) num[i][j]=1;
                else if(now[i][j]=='b'&&p==1) num[i][j]=1;
                else if(now[i][j]=='c'&&p==2) num[i][j]=1;
    }
    inline void Dangle(int p)
    {
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=m;j++)
                l[i][j][p]=r[i][j][p]=j,h[i][j][p]=1;
        for(register int i=1;i<=n;i++)
            for(register int j=2;j<=m;j++)
                if(num[i][j]==num[i][j-1])
                    l[i][j][p]=l[i][j-1][p];
        for(register int i=1;i<=n;i++)
            for(register int j=m-1;j>=1;j--)
                if(num[i][j]==num[i][j+1])
                    r[i][j][p]=r[i][j+1][p];
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=m;j++)
            {
                if(i>1&&num[i][j]==num[i-1][j])
                {
                    l[i][j][p]=max(l[i-1][j][p],l[i-1][j][p]);
                    r[i][j][p]=min(r[i-1][j][p],r[i-1][j][p]);
                    h[i][j][p]=h[i-1][j][p]+1;
                }
                ans=max(ans,(l[i][j][p]-r[i][j][p]+1)*h[i][j][p]);
            }
    }
    int main(){
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(register int i=1;i<=n;i++)
                for(register int j=1;j<=m;j++)
                    cin>>las[i][j],now[i][j]=las[i][j];
            ans=-1;for(register int i=0;i<=n;i++) Change(i),Dangle(i);
            printf("%d
    ",ans);
        }
    }
    /*for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                cout<<las[i][j];
            puts("");
    }*/
    

    说实话,这很让我蒙,上网一查,很多人用的是单调栈,好像这两种做法的区别不大,都可以做这一题。

    后来发现了王知昆(Dalao)论文。里面谈到这两种算法的复杂度有较大区别。原话如下:

    以上说了两种具有一定通用性的处理算法,时间复杂度分别为(O_{S^2})(O_{nm}) 。两种算法分别适用于不同的情况。从时间复杂度上来看,第一种算法对于障碍点稀疏的情况比较有效,第二种算法则与障碍点个数的多少没有直接的关系(当然,障碍点较少时可以通过对障碍点坐标的离散化来减小处理矩形的面积,不过这样比较麻烦,不如第一种算法好),适用于障碍点密集的情况。

    好像有点道理,于是我又将这一题改为了如下的样子:

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    using namespace std;
    const int N=1010;
    char las[N][N],now[N][N];
    int n,m,ans,num[N][N],H[N],top,w[N];
    inline int max(int a,int b) {return a>b?a:b;}
    inline bool Check1(int i,int j){return las[i][j]=='w'||las[i][j]=='y'||las[i][j]=='z';}
    inline bool Check2(int i,int j){return las[i][j]=='w'||las[i][j]=='x'||las[i][j]=='z';}
    inline bool Check3(int i,int j){return las[i][j]=='x'||las[i][j]=='y'||las[i][j]=='z';}
    inline void Change(int p)
    {
        for(register int i=1;i<=n;i++)
            for(register int j=1;j<=m;j++)
    		{
    			num[i][j]=0;
                if(p==0&&(Check1(i,j)||las[i][j]=='a')) num[i][j]=num[i-1][j]+1;
                else if(p==1&&(Check2(i,j)||las[i][j]=='b')) num[i][j]=num[i-1][j]+1;
                else if(p==2&&(Check3(i,j)||las[i][j]=='c')) num[i][j]=num[i-1][j]+1;
    			else num[i][j]=num[i-1][j];
    		}
    	/*for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                cout<<num[i][j];
            puts("");
        }
    	puts("");*/
    }
    inline void Dangle(int p)
    {
    	for(int i=1;i<=n;i++)
    	{
    		top=0;
    		for(int j=1;j<=m+1;j++)
    			if(num[i][j]>H[top]) H[++top]=num[i][j],w[top]=1;
    			else
    			{
    				int wid=0;
    				while(num[i][j]<H[top]) wid+=w[top],ans=max(ans,wid*H[top]),top--;
    				H[++top]=num[i][j],w[top]=wid+1;
    			}
    	}
    }
    int main(){
    #ifndef ONLINE_JUDGEh
        freopen("a.in","r",stdin);
    #endif
        while(scanf("%d%d",&n,&m)>0)
        {
            for(register int i=1;i<=n;i++)
                for(register int j=1;j<=m;j++)
                    cin>>las[i][j],now[i][j]=las[i][j];
            ans=-1;for(register int i=0;i<=n;i++) Change(i),Dangle(i);
            printf("%d
    ",ans);
        }
    }
    /*for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                cout<<las[i][j];
            puts("");
    }*/
    

    具体思路是将每一行的(1)压下来,再按最大子段和来做,但还是(TLE)……那是真的爽

    后来发现一个很有趣的地方,好像内存大了会(TLE)?

    再仔细看一下网上的做法,总体思路没太大问题,还是像悬线一样求(l,r),但有人用单调栈求,有人用跳。

    先咕着……

  • 相关阅读:
    webpack4入门配置
    RequireJs的理解
    js一次控制 多个style样式
    vue中封装一个全局的弹窗js
    地理位置索引 2d索引
    索引属性 稀疏索引,定时索引
    索引属性 unique指定
    索引属性 name指定
    mongodb索引 全文索引使用限制
    mongodb索引 全文索引之相似度查询
  • 原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/10959579.html
Copyright © 2020-2023  润新知