zoukankan      html  css  js  c++  java
  • [BZOJ 1047] [HAOI2007] 理想的正方形 【单调队列】

    题目链接:BZOJ - 1047

    题目分析

    使用单调队列在 O(n^2) 的时间内求出每个 n * n 正方形的最大值,最小值。然后就可以直接统计答案了。

    横向有 a 个单调队列(代码中是 Q[1] 到 Q[a] ),维护每行当前枚举区间的单调队列。

    纵向一个单调队列(代码中是 Q[0] ),求出当前枚举区间的每行的单调队列后,就得到了每行的这个区间的最小值(最大值),就相当于一个长度为行数的数组,然后纵向做单调队列,求出的就是正方形的最值了。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    const int MaxN = 1000 + 5, INF = 999999999;
    
    int a, b, n, Ans;
    int Map[MaxN][MaxN], Q[MaxN][MaxN], F[MaxN], Head[MaxN], Tail[MaxN], Min[MaxN][MaxN], Max[MaxN][MaxN];
    //Q[0]是纵向的单调队列 
    
    inline int gmin(int a, int b) {return a < b ? a : b;}
    inline int gmax(int a, int b) {return a > b ? a : b;}
    
    void Get_Min() 
    {
    	for (int i = 1; i <= a; ++i) 
    	{
    		Head[i] = 1;
    		Tail[i] = 0;
    	}
    	for (int i = 1; i <= b; ++i) 
    	{
    		for (int j = 1; j <= a; ++j) 
    		{
    			if (i > n && Head[j] <= Tail[j] && Q[j][Head[j]] == i - n) ++Head[j];
    			while (Head[j] <= Tail[j] && Map[j][i] < Map[j][Q[j][Tail[j]]]) --Tail[j];
    			Q[j][++Tail[j]] = i; 
    		}	
    		if (i >= n) 
    		{
    			Head[0] = 1; Tail[0] = 0;
    			for (int j = 1; j <= a; ++j)
    			{
    				F[j] = Map[j][Q[j][Head[j]]];
    				if (j > n && Head[0] <= Tail[0] && Q[0][Head[0]] == j - n) ++Head[0];
    				while (Head[0] <= Tail[0] && F[j] < F[Q[0][Tail[0]]]) --Tail[0];
    				Q[0][++Tail[0]] = j;
    				if (j >= n) Min[j][i] = F[Q[0][Head[0]]]; 
    			}
    		}
    	}
    }
    
    void Get_Max() 
    {
    	for (int i = 1; i <= a; ++i) 
    	{
    		Head[i] = 1;
    		Tail[i] = 0;
    	}
    	for (int i = 1; i <= b; ++i) 
    	{
    		for (int j = 1; j <= a; ++j) 
    		{
    			if (i > n && Head[j] <= Tail[j] && Q[j][Head[j]] == i - n) ++Head[j];
    			while (Head[j] <= Tail[j] && Map[j][i] > Map[j][Q[j][Tail[j]]]) --Tail[j];
    			Q[j][++Tail[j]] = i; 
    		}
    		if (i >= n) 
    		{
    			Head[0] = 1; Tail[0] = 0;
    			for (int j = 1; j <= a; ++j)
    			{
    				F[j] = Map[j][Q[j][Head[j]]];
    				if (j > n && Head[0] <= Tail[0] && Q[0][Head[0]] == j - n) ++Head[0];
    				while (Head[0] <= Tail[0] && F[j] > F[Q[0][Tail[0]]]) --Tail[0];
    				Q[0][++Tail[0]] = j;
    				if (j >= n) Max[j][i] = F[Q[0][Head[0]]]; 
    			}
    		}
    	}
    }
    
    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", &Map[i][j]);
    	Get_Min();
    	Get_Max();
    	Ans = INF;
    	for (int i = n; i <= a; ++i) 
    		for (int j = n; j <= b; ++j) 
    			Ans = gmin(Ans, Max[i][j] - Min[i][j]);
    	printf("%d
    ", Ans);
    	return 0;
    }
    

      

  • 相关阅读:
    事件使用(转 )
    使用Dotfuscator 进行.Net代码混淆 代码加密的方法
    Git分布式版本控制系统学习笔记
    免费SVN服务器笔记
    如何设置mysql远程访问及防火墙设置
    c# GridControl怎么换行
    模拟Post登陆带验证码的网站
    c#控制打印机杂项
    ssh 自动登录
    mysql 使用记号
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4319732.html
Copyright © 2011-2022 走看看