AcWing 1091 理想的正方形

題目描述:

有一個 a×b的整數組成的矩陣,現請你從中找出一個 n×n 的正方形區域,使得該區域所有數中的最大值和最小值的差最小。

輸入格式

第一行爲三個整數,分別表示 a,b,n的值;

第二行至第 a+1 行每行爲 b 個非負整數,表示矩陣中相應位置上的數。

輸出格式

輸出僅一個整數,爲 a×b 矩陣中所有“n×n 正方形區域中的最大整數和最小整數的差值”的最小值。

數據範圍

2≤a,b≤1000,
n≤a,n≤b,n≤100,
矩陣中的所有數都不超過 10^9。

輸入樣例:

5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2

輸出樣例:

1

分析:

本題代碼看起來複雜,寫起來卻格外的順,一遍寫完,都不用調試,提交就直接AC了,可能是腦中的模型比較清晰吧。

直接說下思路,先對矩陣逐行求區間長度是n的滑動窗口的最大最小值,分別存進最大最小值數組。然後再對求出的最值數組按列再求一次最值,二者之差就是所有n*n正方形區域內的最大整數與最小整數差了。當然,求滑動區間的最值就是使用單調隊列實現的。

如圖所示,如果要求3*3正方形中的最值,可以先將各行中長度爲3的區間的最值存在這個區間開始的格子裏,比如上圖藍色的區域,求完後,第一列的前三個格子就存了這個藍色正方形各行的最值,再豎着對第一行求下長度爲3區間的最值存到第一個單元格里,第一個單元格存儲的就是整個正方形區域內的最值了。當然我們是先把初始矩形各行長度爲n區間的最值存到一個新的數組,然後再對這個數組豎着求最值存入又一個數組來實現的,以避免值被覆蓋。實現細節見代碼:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
int n,m,k,g[N][N],mi[N][N],ma[N][N],q[N],r[N],s[N],t[N];
void getmin(int *a,int *b,int m){//將a中區間長度是k區間最小值存入b
    int hh = 0,tt = -1;
    for(int i = 1;i <= m;i++){
        if(hh <= tt && i - q[hh] >= k)  hh++;
        while(hh <= tt && a[i] <= a[q[tt]]) tt--;
        q[++tt] = i;
        if(i >= k)  b[i - k + 1] = a[q[hh]];
    }
}
void getmax(int *a,int *b,int m){//將a中區間長度是k區間最大值存入b
    int hh = 0,tt = -1;
    for(int i = 1;i <= m;i++){
        if(hh <= tt && i - q[hh] >= k)  hh++;
        while(hh <= tt && a[i] >= a[q[tt]]) tt--;
        q[++tt] = i;
        if(i >= k)  b[i - k + 1] = a[q[hh]];
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            scanf("%d",&g[i][j]);
    for(int i = 1;i <= n;i++){//將各行的最值分別存入mi和ma數組
        getmin(g[i],mi[i],m);
        getmax(g[i],ma[i],m);
    }
    int res = 1e9;
    for(int i = 1;i <= m - k + 1;i++){
        for(int j = 1;j <= n;j++)   r[j] = mi[j][i];//取一列的最值數組
        getmin(r,s,n);//求最值數組列的最值
        for(int j = 1;j <= n;j++)   r[j] = ma[j][i];
        getmax(r,t,n);
        for(int j = 1;j <= n - k + 1;j++)   res = min(res,t[j] - s[j]);//更新最值之差
    }
    printf("%d\n",res);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章