20130907北邮组队赛小结

A题:变形的扫描法的典型题。

比赛的时候我一看这题就想到了上次做的LA3029,不过那题做的太久了,没有什么印象了,最后悲剧的没有A出来。

先讲下LA3029:点击打开链接

那题是给你一个矩阵,每个格子满或者空,求最大的空的子矩阵。

这题是求满足每行和每列都是等差数列的最大子矩阵。其实做法都差不多,都是朴素的O(m^2*n^2),扫描法简化后O(m*n)的做法。

要注意的地方:

1.维护left,right时要不断更新两个标记量,lo(当前最左的运动极限,列标号表示),ld(当前的从左向右的公差),ro,rd同理。

第一行的时候:不受到上面的行影响,所以得特判。

a[i][j]-a[i][j-1]==ld,left[i][j]=lo,否则ld=a[i][j]-a[i][j-1],lo=j-1,left[i][j]=lo;

不是第一行的时候:

left[i][j]=max(lo,left[i-1][j]);

第一列和第二列也得特判,因为最小要两项才会有公差。

维护right同理。

2.维护up数组,只要求长度就行了,维护ud(列上的公差)。

第一行和第二行:

up[i][j]=i+1;

其他行:

a[i][j]-a[i-1][j]==ud, up[i][j]=up[i-1][j]+1;

否则:up[i][j]=2;ud=a[i][j]-a[i-1][j];

3.特别注意一下,最终子矩阵的表示方法:

不是(right[i][j]-left[i][j]+1)*up[i][j];

必须先判断一下左边和右边是否是同一个等差数列:a[i][j]-a[i][j+1]==a[i][j-1]-a[i][j]

 代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 3005
#define INF 0xfffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll long long
using namespace std;
int a[maxn][maxn],Left[maxn][maxn],Right[maxn][maxn],up[maxn][maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        //维护Left,Right,up
        int lo,ld;
        for(int i=0; i<n; i++)
        {
            lo=0,ld=a[i][1]-a[i][0];
            for(int j=0; j<m; j++)
            {
                if(i==0)//第一行
                {
                    if(j==0||j==1)
                    {
                        Left[i][j]=lo;
                    }
                    else
                    {
                        if(a[i][j]-a[i][j-1]==ld)
                        {
                            Left[i][j]=lo;
                        }
                        else
                        {
                            lo=j-1;
                            ld=a[i][j]-a[i][j-1];
                            Left[i][j]=lo;
                        }
                    }
                }
                else//不是第一行
                {
                    if(j==0||j==1)
                    {
                        Left[i][j]=max(lo,Left[i-1][j]);
                    }
                    else
                    {
                        if(a[i][j]-a[i][j-1]==ld)
                        {
                            Left[i][j]=max(lo,Left[i-1][j]);
                        }
                        else
                        {
                            lo=j-1;
                            ld=a[i][j]-a[i][j-1];
                            Left[i][j]=max(lo,Left[i-1][j]);
                        }
                    }
                }
            }
        }
        //维护Right
        int ro,rd;
        for(int i=0; i<n; i++)
        {
            ro=m-1,rd=a[i][m-2]-a[i][m-1];
            for(int j=m-1; j>=0; j--)
            {
                if(i==0)//第一行
                {
                    if(j==m-1||j==m-2)
                    {
                        Right[i][j]=ro;
                    }
                    else
                    {
                        if(a[i][j]-a[i][j+1]==rd)
                        {
                            Right[i][j]=min(ro,Right[i-1][j]);
                        }
                        else
                        {
                            rd=a[i][j]-a[i][j+1];
                            ro=j+1;
                            Right[i][j]=min(ro,Right[i-1][j]);
                        }
                    }
                }
                else//不是第一行
                {
                    if(j==m-1||j==m-2)
                    {
                        Right[i][j]=min(ro,Right[i-1][j]);
                    }
                    else
                    {
                        if(a[i][j]-a[i][j+1]==rd)
                        {
                            Right[i][j]=min(ro,Right[i-1][j]);
                        }
                        else
                        {
                            rd=a[i][j]-a[i][j+1];
                            ro=j+1;
                            Right[i][j]=min(ro,Right[i-1][j]);
                        }
                    }

                }
            }
        }
        //维护up
        int ud,uo;
        for(int j=0;j<m;j++)//从每一列开始
        {
            ud=a[1][j]-a[0][j];
            for(int i=0;i<n;i++)
            {
                if(i==0||i==1)
                {
                    up[i][j]=i+1;
                }
                else
                {
                    if(a[i][j]-a[i-1][j]==ud)
                    {
                        up[i][j]=up[i-1][j]+1;
                    }
                    else
                    {
                        ud=a[i][j]-a[i-1][j];
                        up[i][j]=2;
                    }
                }
            }
        }
        int ans=0;
        int tt=1;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                int tmp;
                int tmp1=(j-Left[i][j]+1)*up[i][j];
                int tmp2=(Right[i][j]-j+1)*up[i][j];
                tmp=max(tmp1,tmp2);
                if(a[i][j+1]-a[i][j]==a[i][j]-a[i][j-1])
                {
                    int tmp3=(Right[i][j]-Left[i][j]+1)*up[i][j];
                    tmp=max(tmp,tmp3);
                }
                if(tmp>ans)
                {
                    ans=tmp;
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值