1. 程式人生 > >[二維RMQ]luogu 2216 [HAOI2007]理想的正方形

[二維RMQ]luogu 2216 [HAOI2007]理想的正方形

分隔 style ios open space -m eve mes 最大整數

題目描述

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

輸入輸出格式

輸入格式:

第一行為3個整數,分別表示a,b,n的值

第二行至第a+1行每行為b個非負整數,表示矩陣中相應位置上的數。每行相鄰兩數之間用一空格分隔。

輸出格式:

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

輸入輸出樣例

輸入樣例
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,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

分析

暴力打法顯然,設mx[i][j][k]為以i,j為左上角,邊長為k的正方形的最大值(最小值同)

mx[i][j][k]=max(mx[i+1][j+1][k-1],mx[i+1][j][k-1],mx[i][j+1][k-1],rect[i][j])

然而O(1000^3)顯然超

觀察一下,我們發現這個東西很像一個二維的RMQ,那麽改一下

mx[i][j][k]為以i,j為左上角,邊長為2^k的正方形的最大值

mx[i][j][k]=max(mx[i+2^(k-1)][j+2^(k-1)][k-1],mx[i+2^(k-1)][j][k-1],mx[i][j+2^(k-1][k-1],mx[i][j][k-1])

統計也差不多,註意補一下不滿的二進制位和邊界

技術分享圖片
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=1e3+10;
int mx[N][N],mn[N][N];
int a,b,n,logn; int Get_Ans(int i,int j) { int mxx=0,mnn=2147483647; mxx=max(mx[i][j],max(mx[i+n-(1<<logn)][j],max(mx[i][j+n-(1<<logn)],mx[i+n-(1<<logn)][j+n-(1<<logn)]))); mnn=min(mn[i][j],min(mn[i+n-(1<<logn)][j],min(mn[i][j+n-(1<<logn)],mn[i+n-(1<<logn)][j+n-(1<<logn)]))); return mxx-mnn; } int main() { scanf("%d%d%d",&a,&b,&n); for (int i=1;i<=a;i++) for (int j=1;j<=b;j++) { scanf("%d",&mx[i][j]); mn[i][j]=mx[i][j]; } logn=log(n)/log(2); for (int k=1;k<=logn;k++) for (int i=1;i<=a-(1<<k-1);i++) for (int j=1;j<=b-(1<<k-1);j++) mx[i][j]=max(mx[i][j],max(mx[i+(1<<k-1)][j],max(mx[i][j+(1<<k-1)],mx[i+(1<<k-1)][j+(1<<k-1)]))), mn[i][j]=min(mn[i][j],min(mn[i+(1<<k-1)][j],min(mn[i][j+(1<<k-1)],mn[i+(1<<k-1)][j+(1<<k-1)]))); int ans=2147483647; for (int i=1;i<=a-n+1;i++) for (int j=1;j<=b-n+1;j++) ans=min(ans,Get_Ans(i,j)); printf("%d",ans); }
View Code

[二維RMQ]luogu 2216 [HAOI2007]理想的正方形