BZOJ 1047 二维单调队列
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1047
题意:见中文题面
思路:该题是求二维的子矩阵的最大值与最小值的差值尽量小。所以可以考虑求出每个子矩阵的最大值和最小值。考虑一维求子段的最小值/最大值的思路。滑动窗口+单调队列。 转换成二维。设minNum[i][j]表示右下角为(i,j)的子矩阵的最小值。先对矩阵每一行用一维的做法求出每一行的子段的最小值,然后同样的方法求列的最值。注意在求列的子段最小值时比较的元素不是原矩阵的元素而是用行求的结果来比较。 具体看代码吧。
#define _CRT_SECURE_NO_DEPRECATE #include<stdio.h> #include<string.h> #include<cstring> #include<algorithm> #include<queue> #include<math.h> #include<time.h> #include<vector> #include<iostream> #include<string> using namespace std; typedef long long int LL; const int MAXN = 1000 + 10; const int INF = 0x3f3f3f3f; int n, m, k, num[MAXN][MAXN], minNum[MAXN][MAXN], maxNum[MAXN][MAXN]; void solve(int type, int seg[][MAXN]){ deque<pair<int, int> > deq; for (int i = 1; i <= n; i++){ //求行子段的最值。 deq.clear(); for (int j = 1; j <= m; j++){ while (!deq.empty() && j - deq.front().second >= k){ deq.pop_front(); } if (type){ while (!deq.empty() && deq.back().first < num[i][j]){ deq.pop_back(); } } else{ while (!deq.empty() && deq.back().first > num[i][j]){ deq.pop_back(); } } deq.push_back(make_pair(num[i][j], j)); seg[i][j] = deq.front().first; } } for (int j = 1; j <= m; j++){ //求列的最值 deq.clear(); for (int i = 1; i <= n; i++){ while (!deq.empty() && i - deq.front().second >= k){ deq.pop_front(); } if (type){ while (!deq.empty() && deq.back().first < seg[i][j]){ deq.pop_back(); } } else{ while (!deq.empty() && deq.back().first > seg[i][j]){ deq.pop_back(); } } deq.push_back(make_pair(seg[i][j], i)); seg[i][j] = deq.front().first; } } } int main(){ //#ifdef kirito // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); //#endif // int start = clock(); while (~scanf("%d%d%d", &n, &m, &k)){ for (int i = 1; i <= n; i++){ for (int j = 1; j <= m; j++){ scanf("%d", &num[i][j]); } } int ans = INF; solve(0, minNum); solve(1, maxNum); ////Debug //printf("minNum Segment:\n"); //for (int i = 1; i <= n; i++){ // for (int j = 1; j <= m; j++){ // printf("%d ", minNum[i][j]); // } // printf("\n"); //} //printf("maxNum Segment:\n"); //for (int i = 1; i <= n; i++){ // for (int j = 1; j <= m; j++){ // printf("%d ", maxNum[i][j]); // } // printf("\n"); //} for (int i = k; i <= n; i++){ for (int j = k; j <= m; j++){ ans = min(ans, maxNum[i][j] - minNum[i][j]); } } printf("%d\n", ans); } //#ifdef LOCAL_TIME // cout << "[Finished in " << clock() - start << " ms]" << endl; //#endif return 0; }