1. 程式人生 > >二維單調佇列(理想的正方形+修築綠化帶)

二維單調佇列(理想的正方形+修築綠化帶)

 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;
}