题目描述
有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
输入输出格式
输入格式:
第一行为3个整数,分别表示a,b,n的值
第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。
输出格式:
仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。
输入输出样例
说明
问题规模
(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
ST表属于区间dp吧
//正常纯矩阵ST表
#include<bits/stdc++.h> using namespace std; #define maxn 1001 typedef long long ll; #define inf 0x3fffffff int mi[maxn][maxn][11],ma[maxn][maxn][11]; int n,m,k; int l,p; inline char getc(void) { static char buf[1 << 18], *fs, *ft; return (fs == ft && (ft = (fs = buf) + fread(buf, 1, 1 << 18, stdin)), fs == ft) ? EOF : *fs++; } inline int read(void) { char tmp = getc(); int res = 0; for(; !isdigit(tmp); tmp = getc()); for(; isdigit(tmp); tmp = getc()) res = ((res + (res << 2)) << 1) + (tmp ^ 0x30); return res; } int Work(int x,int y) { int x1=x+k-1,y1=y+k-1; int MIN=min( min(mi[x][y][p],mi[x][y1-(1<<p)+1][p]), min(mi[x1-(1<<p)+1][y][p],mi[x1-(1<<p)+1][y1-(1<<p)+1][p]) ); int MAX=max( max(ma[x][y][p],ma[x][y1-(1<<p)+1][p]), max(ma[x1-(1<<p)+1][y][p],ma[x1-(1<<p)+1][y1-(1<<p)+1][p]) ); return MAX-MIN; } int main() { // freopen("test.txt","r",stdin); memset(mi,0x3f,sizeof(mi)); memset(ma,0,sizeof(ma)); n=read(),m=read(),k=read(); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) mi[i][j][0]=ma[i][j][0]=read(); l=log2(min(n,m)),p=log2(k); for(int k=1; k<=l; k++) { int x=1<<(k-1); for(int i=1; i<=n-x; i++) for(int j=1; j<=m-x; j++) { mi[i][j][k]=min( min(mi[i][j][k-1],mi[i+x][j+x][k-1]), min(mi[i][j+x][k-1],mi[i+x][j][k-1]) ); ma[i][j][k]=max( max(ma[i][j][k-1],ma[i+x][j][k-1]), max(ma[i][j+x][k-1],ma[i+x][j+x][k-1]) ); } } int ans=inf; for(int i=1; i<=n-k+1; i++) for(int j=1; j<=m-k+1; j++) ans=min(ans,Work(i,j)); cout<<ans; return 0; }
//单调队列+ST表
#include<cstdio> #include<iostream> #include<cmath> #define For(i,x,y) for (int i=x;i<=y;i++) #define N 1010 #define inf 1<<30 using namespace std; int a[N][N],ma[N][N][11],mi[N][N][11],qmi[N][2],qma[N][2]; int n,m,t,h1,t1,h2,t2,ans,x; inline int read() { int x=0; int ch=getchar(),f=1; while (!isdigit(ch)&&(ch!=‘-‘)&&(ch!=EOF)) ch=getchar(); if (ch==‘-‘) { f=-1; ch=getchar(); } while (isdigit(ch)) { x=(x<<1)+(x<<3)+ch-‘0‘; ch=getchar(); } return x*f; } //读优 int queryma(int k,int x,int y) { int l=log2(y-x+1); return max(ma[k][x][l],ma[k][y-(1<<l)+1][l]); } //查询第k行的第x列到第y列的最大值 int querymi(int k,int x,int y) { int l=log2(y-x+1); return min(mi[k][x][l],mi[k][y-(1<<l)+1][l]); } //查询第k行的第x列到第y列的最小值 int main() { n=read(),m=read(),t=read(); int l=log2(max(n,m)); For(i,1,n) For(j,1,m) { a[i][j]=read(); ma[i][j][0]=mi[i][j][0]=a[i][j]; For(k,1,10) mi[i][j][k]=inf; } For(i,1,n) For(k,1,l) { x=1<<(k-1); For(j,1,m-x) { ma[i][j][k]=max(ma[i][j][k-1],ma[i][j+x][k-1]); mi[i][j][k]=min(mi[i][j][k-1],mi[i][j+x][k-1]); } } //求ma和mi数组 ans=inf; For(i,1,m) { if (i+t-1>m) break; h1=h2=1,t1=t2=0; For(j,1,n) { x=queryma(j,i,i+t-1); while (x>=qma[t1][1]&&h1<=t1) t1--; qma[++t1][1]=x; qma[t1][0]=j; x=querymi(j,i,i+t-1); while (x<=qmi[t2][1]&&h2<=t2) t2--; qmi[++t2][1]=x; qmi[t2][0]=j;//两个单调队列维护最大值和最小值 if (j>=t) { while (j-t>=qma[h1][0]) h1++; while (j-t>=qmi[h2][0]) h2++; if (qma[h1][1]-qmi[h2][1]<ans) ans=qma[h1][1]-qmi[h2][1]; } } } printf("%d",ans); return 0; }