二维单调队列(理想的正方形+修筑绿化带)

 P2216 [HAOI2007]理想的正方形

题目描述

有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

输入输出格式

输入格式:

 

第一行为3个整数,分别表示a,b,n的值

第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。

 

输出格式:

 

仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。

 

输入输出样例

输入样例#1: 复制
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
输出样例#1: 复制
1

说明

问题规模

(1)矩阵中的所有数都不超过1,000,000,000

(2)20%的数据2<=a,b<=100,n<=a,n<=b,n<=10

(3)100%的数据2<=a,b<=1000,n<=a,n<=b,n<=100

 

题解

很板子的二位单调队列,我们要维护二维区间最小值和二维区间最大值,我们可以想到将未知化为已知,如果用一维单调队列可以做出来就好了

我们第一次将每行每n个数之中的最小值求出,装到另外一个数组里,再将每列每n个数的最小值求出,再装到一个数组里,就好了

最大值同理

不太清楚?举个栗子

例如最小值求法:

3*3的矩阵,n=2

12 3 34

2 3 4

1 0 23

先将每行的每n个数的最小值求出:

3 3

2 3

0 0

然后:

2 3

0 0

这样每个n*n的块的最小值就求好了

上代码

注:Q表示最大值,P表示最小值

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
deque<pair<int,int> > Q,P;
int a,b,n,x[1050][1050],y[1050][1050],z[1050][1050],u[1050][1050],v[1050][1050],maxn=1999999999;
int main(){
    scanf("%d%d%d",&a,&b,&n);
    rep(i,1,a) rep(j,1,b) scanf("%d",&x[i][j]);
    rep(i,1,a){
        Q.clear(); P.clear();
        rep(j,1,n){
            while(!Q.empty() && Q.back().second<=x[i][j]) Q.pop_back();
            Q.push_back(make_pair(j,x[i][j]));
            while(!P.empty() && P.back().second>=x[i][j]) P.pop_back();
            P.push_back(make_pair(j,x[i][j]));
        }
        rep(j,1,b-n+1){
        	y[i][j]=Q.front().second; u[i][j]=P.front().second;
        	while(!Q.empty() && Q.front().first+n-1<j+n) Q.pop_front();
        	while(!Q.empty() && Q.back().second<=x[i][j+n]) Q.pop_back();
            Q.push_back(make_pair(j+n,x[i][j+n]));
            while(!P.empty() && P.front().first+n-1<j+n) P.pop_front();
            while(!P.empty() && P.back().second>=x[i][j+n]) P.pop_back();
            P.push_back(make_pair(j+n,x[i][j+n]));
		}
    }
    rep(j,1,b-n+1){
        Q.clear(); P.clear();
        rep(i,1,n){
            while(!Q.empty() && Q.back().second<=y[i][j]) Q.pop_back();
            Q.push_back(make_pair(i,y[i][j]));
            while(!P.empty() && P.back().second>=u[i][j]) P.pop_back();
            P.push_back(make_pair(i,u[i][j]));
        }
        rep(i,1,a-n+1){
        	v[i][j]=Q.front().second; z[i][j]=P.front().second;maxn=min(maxn,v[i][j]-z[i][j]);
        	while(!Q.empty() && Q.front().first+n-1<i+n) Q.pop_front();
        	while(!Q.empty() && Q.back().second<=y[i+n][j]) Q.pop_back();
            Q.push_back(make_pair(i+n,y[i+n][j]));
            while(!P.empty() && P.front().first+n-1<i+n) P.pop_front();
            while(!P.empty() && P.back().second>=u[i+n][j]) P.pop_back();
            P.push_back(make_pair(i+n,u[i+n][j]));
		}
    }cout<<maxn;
    return 0;
}

  

P2219 [HAOI2007]修筑绿化带

题目描述

为了增添公园的景致,现在需要在公园中修筑一个花坛,同时在画坛四周修建一片绿化带,让花坛被绿化带围起来。

如果把公园看成一个M*N的矩形,那么花坛可以看成一个C*D的矩形,绿化带和花坛一起可以看成一个A*B的矩形。

如果将花园中的每一块土地的“肥沃度”定义为该块土地上每一个小块肥沃度之和,那么,

绿化带的肥沃度=A*B块的肥沃度-C*D块的肥沃度

为了使得绿化带的生长得旺盛,我们希望绿化带的肥沃度最大。

输入输出格式

输入格式:

 

第一行有6个正整数M,N,A,B,C,D

接下来一个M*N的数字矩阵,其中矩阵的第i行j列元素为一个整数Xij,表示该花园的第i行第j列的土地“肥沃度”。

 

输出格式:

 

一个正整数,表示绿化带的最大肥沃程度。

 

输入输出样例

输入样例#1: 
4 5 4 4 2 2
20 19 18 17 16
15 14 13 12 11
10 9 8 7 6
5 4 3 2 1
输出样例#1: 
132

说明

数据范围

30%的数据,1<=M,N<=50

100%的数据,1<=M,N<=1000,1<=A<=M,1<=B<=N,1<=C<=A-2,1<=D<=B-2,1<=“肥沃度”<=100

/*和理想的正方形这道题比较类似
首先我们把所有a*b块的和用mx数组记录下来,这里需要先记录一个每行的前缀和,然后运用队列思想求出
然后把所有c*d块的和求出,记为mn数组,方法同上 
然后的题目就变成了求每个a*b块-(a-2*b-2)块中每个c*d块的最小值(注意不能贴边) 
然而后者我们可以用一个与理想的正方形一模一样的两次单调队列求出
这个循环终值要仔细的推一推
详见代码 
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define rep(i,a,b) for(long long i=a;i<=b;i++)
using namespace std;
typedef long long ll;
deque<pair<ll,ll> > Q;
ll n,m,a,b,c,d,map[1050][1050],s[1050][1050],mx[1050][1050],mn[1050][1050],sum,x[1050][1050],y[1050][1050],maxn;
int main(){
    scanf("%lld%lld%lld%lld%lld%lld",&m,&n,&a,&b,&c,&d);
    rep(i,1,m) rep(j,1,n) scanf("%lld",&map[i][j]),s[i][j]=s[i][j-1]+map[i][j];
    rep(j,b,n){  
        sum=0;
        rep(i,0,a-1)sum+=s[i][j]-s[i][j-b];
        rep(i,a,m){
            sum=sum-s[i-a][j]+s[i-a][j-b];
            sum=sum+s[i][j]-s[i][j-b];
            mx[i-a+1][j-b+1]=sum;
        }
    }
    rep(j,d,n){
        sum=0;
        rep(i,0,c-1)sum+=s[i][j]-s[i][j-d];
        rep(i,c,m){
            sum=sum-s[i-c][j]+s[i-c][j-d];
            sum=sum+s[i][j]-s[i][j-d];
            mn[i-c+1][j-d+1]=sum;
        }
    }
    rep(i,1,m-c+1){
        while(!Q.empty()) Q.pop_back();
        rep(j,1,b-d-1){
            while(!Q.empty() && Q.back().second>=mn[i][j]) Q.pop_back();
            Q.push_back(make_pair(j,mn[i][j]));
        }
        rep(j,1,n-b+3){
            x[i][j]=Q.front().second;
            while(!Q.empty() && Q.front().first+(b-d-1)<=b-d+j-1) Q.pop_front();
            while(!Q.empty() && Q.back().second>=mn[i][j+b-d-1]) Q.pop_back();
            Q.push_back(make_pair(b-d+j-1,mn[i][b-d+j-1]));
        }
    }
	
    rep(j,1,n-b+3){
    	while(!Q.empty()) Q.pop_back();
        rep(i,1,a-c-1){
            while(!Q.empty() && Q.back().second>=x[i][j]) Q.pop_back();
            Q.push_back(make_pair(i,x[i][j]));
        }
        rep(i,1,m-a+3){
            y[i][j]=Q.front().second;
            while(!Q.empty() && Q.front().first+(a-c-1)<=a-c+i-1) Q.pop_front();
            while(!Q.empty() && Q.back().second>=x[i+a-c-1][j]) Q.pop_back();
            Q.push_back(make_pair(a-c+i-1,x[a-c+i-1][j]));
        }
	rep(i,1,m-a+1) rep(j,1,n-b+1) maxn=max(maxn,mx[i][j]-y[i+1][j+1]);
	printf("%lld",maxn);
    return 0;
}

  

posted @ 2018-12-19 16:21  niolle  阅读(677)  评论(0编辑  收藏  举报