1. 程式人生 > >P2216 [HAOI2007]理想的正方形 - 單調隊列

P2216 [HAOI2007]理想的正方形 - 單調隊列

求最大值 print init struct 窗口 https 最大 預處理 答案

這種矩形問題常用單調隊列優化枚舉(通過貪心取最優降低了一個維度的枚舉)
推薦這道題也要做一做:[ZJOI2007]棋盤制作
單調隊列的空間記得開大點! 反正內存夠用
註意,這題正方形邊長是固定的!
暴力算法是枚舉上邊界所在的行,左邊界所在的列,通過這兩個個信息確定一個正方形,然後預處理出一行中從第i個點到i+n個點的最值,再掃一遍這個正方形的行,復雜度是N^3

預處理的時候,復雜度也是N^3。。。考慮用單調隊列來維護,枚舉到一行,看做一個序列,求長度為n的區間最值,就是一個滑動窗口,如果不大明白的話去做一下這道題:P1886 滑動窗口

求答案的時候,也可以用單調隊列優化掉一層枚舉,那麽優化掉行的枚舉吧。行不再需要枚舉,只需要在每列枚舉的時候維護單調隊列,因為正方形邊長是固定的

,只需要知道所在的列是什麽,就可以知道所在的行

以求最大值為例:枚舉到一列的時候,直接掃一遍行,我們不是預處理出一個f[i][j]表示包括第i行第j列的那個元素往後n個元素的最值了麽,把他放到一個單調遞減的隊列裏面,並且維護隊列長度不超過n,此時這個正方形的最大值就是隊頭。

仔細想想,若目前有一個元素比隊列裏面的元素都要大,那麽在以後求最大值的時候我便不需要知道這個元素之前的那些元素具體是什麽,因為目前有個更大且能生存更長時間的元素,所以前面的那些都可以扔掉,直接出隊就好了,維護時效性與最優性

!!!註意,更新答案之前必須保證隊列裏面有且僅有n個元素,也就是說要加一句 i >= n 的特判,不然就會導致還沒構成一個正方形就把答案更新了的錯誤情況

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 1000 + 10;
const int INF = 1 << 30;
typedef long long ll;
int n,m,a,b,ans=INF,max_l,max_r,min_l,min_r;
int f[MAXN][MAXN],g[MAXN][MAXN],rec[MAXN][MAXN];
struct st{
    int val, pos;
}qmax[2*MAXN],qmin[2*MAXN];

void max_modify(int k, int pos) {
    while(max_l <= max_r && qmax[max_r].val <= k) max_r--;
    qmax[++max_r].val = k, qmax[max_r].pos = pos;
    while(max_l <= max_r && abs(qmax[max_r].pos-qmax[max_l].pos) + 1 > n) max_l++;
}

void min_modify(int k, int pos) {
    while(min_l <= min_r && qmin[min_r].val >= k) min_r--;
    qmin[++min_r].val = k, qmin[min_r].pos = pos;
    while(min_l <= min_r && abs(qmin[min_r].pos-qmin[min_l].pos) + 1 > n) min_l++;
}

void init() {
    
    for(int i=1; i<=a; i++) {
        max_l = 1, max_r = 0; // 重置隊列,不然的話空間就得開N^2大小,要不然就會RE,仔細想想為什麽
        min_l = 1, min_r = 0;
        for(int j=b; j; j--) {
            max_modify(rec[i][j], j);
            min_modify(rec[i][j], j); // 每個點進隊一次 出隊一次
            f[i][j] = qmax[max_l].val;
            g[i][j] = qmin[min_l].val;
        }
    }
}

void work() {
    for(int j=n; j<=b; j++) {
        int pos = j - n + 1;
        max_l = 1, max_r = 0;
        min_l = 1, min_r = 0;
        for(int i=1; i<=a; i++) {
            max_modify(f[i][pos], i);
            min_modify(g[i][pos], i);
            if(i >= n) ans = min(ans, qmax[max_l].val - qmin[min_l].val);
        }
    }
}

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", &rec[i][j]);
        }
    }
    init();
    work();
    printf("%d\n", ans);
    return 0;
}

P2216 [HAOI2007]理想的正方形 - 單調隊列