zoukankan      html  css  js  c++  java
  • [HAOI2007] 理想的正方形

    洛谷题目链接:[HAOI2007]理想的正方形

    题目描述

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

    输入输出格式

    输入格式:

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

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

    输出格式:

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

    输入输出样例

    输入样例#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*m)的矩阵中找一个大小为(k)的正方形使得这个正方形内的最大值与最小值的差值最小.

    题解: 首先分析一下数据范围,显然这个算法的复杂度是(O(n^2))的,然而这里需要统计的是最值,无法使用前缀和,所以这里考虑使用单调队列来维护.

    既然是一个正方形,那么很显然在枚举到这个正方形的左上角的顶点的时候,这个正方形就已经可以确定了.所以我们可以竖着先求一遍最大/最小值,然后再用这个最大/最小值来求出一个正方形中的最大/最小值.

    这里将每个位置向下(k)格的最大值/最小值用(Mx[i][j]/Mn[i][j])存起来(即(Mx[i][j] = max(a[i][j], a[i+1][j], ... , a[i+k-1][j])), (Mn[i][j])同理).

    其实说白了就是先将每个位置求出连续的(k)个,也即是竖着求一遍滑动窗口,然后把这些连续的(k)个都看做一个个的格子,再横着求一遍滑动窗口.这个可以好好想想为什么.

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1000+5;
    const int inf=2147483647;
    
    int n, m, k, a[N][N], Mx[N][N], Mn[N][N], mx[N], mn[N], h1, t1, h2, t2, ans = inf;
    
    int main(){
    	ios::sync_with_stdio(false);
    	cin >> n >> m >> k;
    	for(int i=1; i<=n; i++)
    		for(int j=1; j<=m; j++)
    			cin >> a[i][j];
    	
    	for(int i=1; i<=m; i++){//竖着求最大值,保存到Mx[i][j]/Mn[i][j]中
    		h1 = h2 = 1, t1 = t2 = 0;
    		for(int j=1; j<=n; j++){
    			while(h1 <= t1 && a[mx[t1]][i] <= a[j][i]) t1--;
    			while(h2 <= t2 && a[mn[t2]][i] >= a[j][i]) t2--;
    			mx[++t1] = mn[++t2] = j;
    			while(h1 <= t1 && mx[h1]+k <= mx[t1]) h1++;
    			while(h2 <= t2 && mn[h2]+k <= mn[t2]) h2++;
    			if(j >= k) Mx[j-k+1][i] = a[mx[h1]][i], Mn[j-k+1][i] = a[mn[h2]][i];
    		}
    	}
    	
    	for(int i=1; i<=n-k+1; i++){
    		h1 = h2 = 1, t1 = t2 = 0, mx[t1] = mn[t2] = 0;
    		for(int j=1; j<=m; j++){
    			while(h1 <= t1 && Mx[i][mx[t1]] <= Mx[i][j]) t1--;
    			while(h2 <= t2 && Mn[i][mn[t2]] >= Mn[i][j]) t2--;
    			mx[++t1] = mn[++t2] = j;
    			while(h1 <= t1 && mx[h1]+k <= mx[t1]) h1++;
    			while(h2 <= t2 && mn[h2]+k <= mn[t2]) h2++;
    			if(j >= k) ans = min(ans, Mx[i][mx[h1]]-Mn[i][mn[h2]]);
    		}
    	}
    	
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    JeePlus:代码生成器
    JeePlus:API工具
    Java实现 洛谷 P1023 税收与补贴问题
    Java实现 洛谷 P1023 税收与补贴问题
    Java实现 洛谷 P1023 税收与补贴问题
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
  • 原文地址:https://www.cnblogs.com/BCOI/p/9166730.html
Copyright © 2011-2022 走看看