• P2216 [HAOI2007]理想的正方形


    Link

    题目描述

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

    输入格式

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

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

    输出格式

    仅一个整数,为 (a imes b) 矩阵中所有“ (n imes 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) 的这一块矩形中的最大值与最小值。

    你可以理解为,把 (n) 个长度为 (n) 的区间拼在了一起。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int ans = 2147483647;
    int n,m,k,a[1010][1010],q1[1010],q2[1010];
    int maxn[1010][1010],minn[1010][1010];
    inline int read()
    {
    	int s = 0,w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    int main()
    {
    	n = read(); m = read(); k = read();
    	for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) a[i][j] = read();
    	for(int i = 1; i <= n; i++)
    	{
    		int l = 1, r = 0, L = 1, R = 0;
    		q1[++r] = 1; q2[++R] = 1;
    		for(int j = 2; j <= m; j++)//对每一行求一遍滑动窗口
    		{
    			while(l <= r && q1[l] <= j-k) l++;
    			while(L <= R && q2[L] <= j-k) L++;
    			while(l <= r && a[i][q1[r]] <= a[i][j]) r--;
    			while(L <= R && a[i][q2[R]] >= a[i][j]) R--;
    			q1[++r] = j; q2[++R] = j; 
    			maxn[i][j] = a[i][q1[l]];
    			minn[i][j] = a[i][q2[L]];	
    		}
    	}
    	for(int i = k; i <= m; i++)
    	{
    		int l = 1, r = 0, L = 1, R = 0;
    		q1[++r] = 1; q2[++R] = 1;
    		for(int j = 2; j <= n; j++)//在每一行的基础上在对每一列做一遍滑动窗口
    		{
    			while(l <= r && q1[l] <= j-k) l++;
    			while(L <= R && q2[L] <= j-k) L++;
    			while(l <= r && maxn[q1[r]][i] <= maxn[j][i]) r--;
    			while(L <= R && minn[q2[R]][i] >= minn[j][i]) R--;
    			q1[++r] = j; q2[++R] = j;
    			if(j >= k) ans = min(ans,maxn[q1[l]][i] - minn[q2[L]][i]);
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    使用 awk 命令统计文本
    Mysql基础及系统函数(分享)
    存储过程 :字段按逗号拆分并插入到关联表
    Spring Security SavedRequestAwareAuthenticationSuccessHandler类
    自制Springboot Starter
    vue3 input中回车生成标签
    NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
    Educational Codeforces Round 129 (Rated for Div. 2)
    Panasonic Programming Contest 2022(AtCoder Beginner Contest 251)
    2022 Google Kick Start Round C
  • 原文地址:https://www.cnblogs.com/genshy/p/13693795.html
Copyright © 2020-2023  润新知