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

    Description

    BZOJ1047

    Solution

    首先易得暴力DP:(f[i][j][k])表示以(i,j)为左上角的边长为k的正方形的最大值(或最小值),可得(f[i][j][k] = min{a[i][j], f[i+1][j][k-1], f[i][j+1][k-1], f[i+1][j+1][k-1]}),复杂度高达(O(abn)),数量级约为(10^9),无法通过数据。

    考虑优化,由于是最值问题,即RMQ问题,由此可以想到ST表,复杂度(O(ablog n)),基本能卡过时限,但是空间限制比较悬,还需要改进(其实用滚动数组就能优化到能过)。

    继续优化,考虑区间最值问题的另一种解决办法:单调队列,首先将行分别维护单调队列,求出第i行中区间([j,j+n-1])中的最值,记为(m[i][j]),然后对m中的每一列维护单调队列,求出第j列中区间([i,i+n-1])的最值,记为(f[i][j]),则(f[i][j])为以(i,j)为左上角的边长为k的正方形的最值,直接统计答案即可,时空复杂度均为(O(ab)),已经足够优秀。

    Code

    #include <cstdio>
    #include <algorithm>
    
    const int N = 1010;
    
    int a[N][N], mxa[N][N], mna[N][N], mxb[N][N], mnb[N][N];
    int mx[N], mxhd, mxtl, mn[N], mnhd, mntl, mxp[N], mnp[N];
    int n, m, k;
    
    int main() {
    	scanf("%d%d%d", &n, &m, &k);
    	for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) scanf("%d", &a[i][j]);
    	for (int i = 1; i <= n; ++i) {
    		mxhd = mxtl = mnhd = mntl = 0;
    		for (int j = 1; j <= m; ++j) {
    			while (mxtl != mxhd && mx[mxtl-1] <= a[i][j]) mxtl--;
    			mx[mxtl] = a[i][j]; mxp[mxtl] = j; mxtl++;
    			if (mxp[mxhd] == j - k) mxhd++;
    			if (j >= k) mxa[i][j] = mx[mxhd];
    
    			while (mntl != mnhd && mn[mntl-1] >= a[i][j]) mntl--;
    			mn[mntl] = a[i][j]; mnp[mntl] = j; mntl++;
    			if (mnp[mnhd] == j - k) mnhd++;
    			if (j >= k) mna[i][j] = mn[mnhd];
    		}
    	}
    	int ans = 0x3f3f3f3f;
    	for (int j = k; j <= m; ++j) {
    		mxhd = mxtl = mnhd = mntl = 0;
    		for (int i = 1; i <= n; ++i) {
    			while (mxtl != mxhd && mx[mxtl-1] <= mxa[i][j]) mxtl--;
    			mx[mxtl] = mxa[i][j]; mxp[mxtl] = i; mxtl++;
    			if (mxp[mxhd] == i - k) mxhd++; // mark
    			if (i >= k) mxb[i][j] = mx[mxhd];
    
    			while (mntl != mnhd && mn[mntl-1] >= mna[i][j]) mntl--;
    			mn[mntl] = mna[i][j]; mnp[mntl] = i; mntl++;
    			if (mnp[mnhd] == i - k) mnhd++; // mark
    			if (i >= k) mnb[i][j] = mn[mnhd];
    		}
    		for (int i = k; i <= n; ++i) {
    			ans = std::min(ans, mxb[i][j] - mnb[i][j]);
    		}
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    

    Note

    mark那里将i错写成j,导致调了好久,还是要注意。

  • 相关阅读:
    Linux input子系统学习总结(一)---- 三个重要的结构体
    DRM/KMS 基本组件介绍
    Framebuffer 驱动学习总结(二)---- Framebuffer模块初始化
    Framebuffer 驱动学习总结(一) ---- 总体架构及关键结构体
    Linux USB驱动学习总结(三)---- USB鼠标的加载、初始化和通信过程
    Linux USB驱动学习总结(一)---- USB基本概念及驱动架构
    使用Python调用动态库
    使用 SignalR与SSE(Sever sent event)向客户端推送提示信息
    在IDEA下使用Spring Boot的热加载(Hotswap)
    使用Spring boot + jQuery上传文件(kotlin)
  • 原文地址:https://www.cnblogs.com/wyxwyx/p/bzoj1047.html
Copyright © 2011-2022 走看看