Planting Trees

Planting Trees

时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

The semester is finally over and the summer holiday is coming. However, as part of your university's graduation requirement, you have to take part in some social service during the holiday. Eventually, you decided to join a volunteer group which will plant trees in a mountain.
To simplify the problem, let's represent the mountain where trees are to be planted with an N×N grid. Let's number the rows  1 to N from top to bottom, and number the columns 1 to  N from left to right. The elevation of the cell in the i-th row and j-th column is denoted by ai,j. Your leader decides that trees should be planted in a rectangular area within the mountain and that the maximum difference in elevation among the cells in that rectangle should not exceed M. In other words, if the coordinates of the top-left and the bottom-right corners of the rectangle are (x1,y1) and (x2,y2), then the condition ∣ai,j−ak,l∣≤M must hold for x1≤i,k≤x2, y1≤j,l≤y2. Please help your leader calculate the maximum possible number of cells in such a rectangle so that he'll know how many trees will be planted.

输入描述:

The input contains multiple cases. The first line of the input contains a single integer T (1≤T≤1000), the number of cases.
For each case, the first line of the input contains two integers N (1≤N≤500)and M (0≤M≤105). The following N lines each contain N integers, where the  j-th integer in the  i-th line denotes ai,j (1≤ai,j≤105).
It is guaranteed that the sum of N3N^3N3 over all cases does not exceed 25⋅1e7 .

输出描述:

For each case, print a single integer, the maximum number of cells in a valid rectangle.

输入

2
2 0
1 2
2 1
3 1
1 3 2
2 3 1
3 2 1

输出

1
4

链接:https://ac.nowcoder.com/acm/contest/883/F
来源:牛客网

题意:在一个二维数字矩阵里选择一个子矩阵,且子矩阵里任意两数差的绝对值小于等于M,求子矩阵最大面积。
思路1:考虑以前做过的一道单调栈求最大全1子矩阵的题目,本题也可以转化为这个问题,即每次保证大矩阵里面的任意两数差的绝对值小于等于M,然后将保留下来的数变为1,其余数变为0,求这个矩阵的最大全1子矩阵。具体实现方法就是将矩阵里的数按照权值排序,维护两个左右指针,每次删除一些数的同时再加上一些数,但总保证当前矩阵里的数差的绝对值小于等于M。还一个细节问题是:每次修改数的时候,只会有特定的几行会影响到答案,于是我们就只需要跑这几行就行了,不需要对整个矩阵跑最大全1子矩阵。

#include <bits/stdc++.h>
using namespace std;
  
const int N =550;
  
long long check(int a[],int n)
{
    int p;
    long long ans=0;
    a[n+1]=p=0;
    int s[N]={0};
    int w[N]={0};
    for(int i=1;i<=n+1;i++)
    {
        if(a[i]>s[p])
        {
            s[++p] = a[i];
            w[p] = 1;
        }
        else
        {
            int width=0;
            while(s[p]>a[i])
            {
                width+=w[p];
                ans=max(ans,(long long)width*s[p]);
                p--;
            }
            s[++p]=a[i];
            w[p]=width+1;
        }
  
    }
    return ans;
}
  
struct ss
{
    int x,y,a;
  
    bool operator < (const ss & s) const
    {
        return a<s.a;
    }
};
  
ss arr[N*N];
int Map[N][N];
int vis[N]={0};
  
void deal(int x,int y,int n,int type)//修改一个数,它下面的数也会有影响
{
    if(type==0)
    {
        Map[x][y]=0;
        x++;
  
        while(x<=n&&Map[x][y])
        {
            Map[x][y]=Map[x-1][y]+1;
            x++;
        }
  
    } else
    {
        vis[x]=1;
        Map[x][y]=Map[x-1][y]+1;
        x++;
  
        while(x<=n&&Map[x][y])
        {
            vis[x]=1;
            Map[x][y] = Map[x - 1][y] + 1;
            x++;
        }
    }
}
  
  
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d %d",&n,&m);
        long long ans=1;
        int tot=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                Map[i][j]=0;
                scanf("%d",&arr[++tot].a);
                arr[tot].x=i;
                arr[tot].y=j;
            }
        }
  
        sort(arr+1,arr+1+tot);
        int l=1,r=2;
  
        deal(arr[1].x,arr[1].y,n,1);
  
        while(r<=n*n)
        {
            while(r<=n*n&&arr[r].a-arr[l].a<=m)
            {
                deal(arr[r].x,arr[r].y,n,1);//增加1个数
                r++;
            }
 
            for(int i=1;i<=n;i++)
            {
                if(vis[i])//对所有可能对答案有影响的行跑最大全1子矩阵
                {
                    vis[i]=0;
                    ans=max(ans,check(Map[i],n));
                }
            }
  
            int last=l;
            while(l<r&&arr[l].a==arr[last].a)//删除一个数
            {
                deal(arr[l].x,arr[l].y,n,0);
                l++;
            }
  
        }
        printf("%lld\n",ans);
    }
    return 0;
}
解法1

思路2:枚举子矩形的上下边界,同时维护对于当前枚举的上下边界的每列的最大最小值。确定了上下界后,枚举子矩形的右边界,然后考虑用单调栈快速求得可行的最小左边界。枚举右边界的同时维护两个单调队列,分别维护最大值和最小值,最小值的单调队列下标递增,大小递增,最大值为下标递增,大小递减。使用这两个单调队列就可以维护一个当前可行的最小左边界。

#include <bits/stdc++.h>
#define inf (INT_MAX/2)
using namespace std;

const int N = 550;
int arr[N][N];

int ma[N],mi[N];

struct ss
{
    int v,index;
};

struct Deque{

    ss arr[N];
    int l,r;

    Deque()
    {
        l=0;
        r=0;
    }

    void clear()
    {
        l=r=0;
    }

    void push_back(ss now)
    {
        arr[r++]=now;
    }
    void pop_back()
    {
        r--;
    }
    void pop_front()
    {
        l++;
    }
    ss front()
    {
        return arr[l];
    }
    ss back()
    {
        return arr[r-1];
    }

    bool empty()
    {
        return l==r;
    }
};


Deque mas,mis;

void clear()
{
    mas.clear();
    mis.clear();
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d %d",&n,&m);

        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                scanf("%d",&arr[i][j]);

        int ans=0;

        for(int upper=1; upper<=n; upper++)
        {
            for(int i=1; i<=n; i++)
            {
                ma[i]=0;
                mi[i]=inf;
            }

            for(int down=upper; down<=n; down++)
            {
                clear();
                for(int i=1; i<=n; i++)
                {
                    ma[i]=max(ma[i],arr[down][i]);
                    mi[i]=min(mi[i],arr[down][i]);

                    if(ma[i]-mi[i]>m)
                    {
                        clear();
                        continue;
                    }
                    int now=i;
                    while(!mas.empty()&&mas.back().v<=ma[i])now=mas.back().index,mas.pop_back();//该数比右边界的最大值小,那么右边界的最大值若是能被包含进一个子矩阵,该数也一定可以被包含
                    while(!mis.empty()&&abs(ma[i]-mis.front().v)>m)mis.pop_front();//不满足条件的删除
                    mas.push_back((ss){ma[i],now});

                    now=i;
                    while(!mis.empty()&&mis.back().v>=mi[i])now=mis.back().index,mis.pop_back();//该数比右边界的最小值大,那么右边界的最小值若是能被包含进一个子矩阵,该数也一定可以被包含
                    while(!mas.empty()&&abs(mas.front().v-mi[i])>m)mas.pop_front();
                    mis.push_back((ss){mi[i],now});
                    ans=max(ans,(i+1-max(mis.front().index,mas.front().index))*(-upper+down+1));
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
解法2
 
    

猜你喜欢

转载自www.cnblogs.com/tian-luo/p/11249240.html