【BZOJ】【1047】【HAOI2007】理想的正方形

DP/单调队列优化



  一眼看上去就是DP

  我想的naive的二维DP是酱紫滴:

    mx[i][j][k]表示以(i,j)为右下角的k*k的正方形区域内的最大值,mn[i][j][k]同理

    mx[i][j][k]=max(v[i][j],max(v[i-k+1][j-k+1],max(mx[i-1][j][k-1],mx[i][j-1][k-1]))),mn[i][j][k]同理

  这个DP是既爆空间又爆时间的……我把空间优化了一下:由于转移的时候只用到了i-1行的DP值,所以可以用滚动数组。

  但是时间上我优化不来了……

 1 //BZOJ 1047
 2 #include<vector>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<iostream>
 7 #include<algorithm>
 8 #define rep(i,n) for(int i=0;i<n;++i)
 9 #define F(i,j,n) for(int i=j;i<=n;++i)
10 #define D(i,j,n) for(int i=j;i>=n;--i)
11 #define pb push_back
12 using namespace std;
13 inline int getint(){
14     int v=0,sign=1; char ch=getchar();
15     while(ch<‘0‘||ch>‘9‘){ if (ch==‘-‘) sign=-1; ch=getchar();}
16     while(ch>=‘0‘&&ch<=‘9‘){ v=v*10+ch-‘0‘; ch=getchar();}
17     return v*sign;
18 }
19 const int N=1010,M=110,INF=~0u>>2;
20 typedef long long LL;
21 /******************tamplate*********************/
22 int a,b,n,ans=INF;
23 int mx[2][N][M],mn[2][N][M],v[N][N];
24 int main(){
25 #ifndef ONLINE_JUDGE
26     freopen("1047.in","r",stdin);
27     freopen("1047.out","w",stdout);
28 #endif
29     a=getint(); b=getint(); n=getint();
30     F(i,1,a) F(j,1,b) v[i][j]=getint();
31     F(i,1,a){
32         int now=i&1;
33         F(j,1,b){
34             mx[now][j][1]=mn[now][j][1]=v[i][j];
35             F(k,2,min(n,min(i,j))){
36                 mx[now][j][k]=max(v[i][j],max(v[i-k+1][j-k+1],max(mx[now][j-1][k-1],mx[now^1][j][k-1])));
37                 mn[now][j][k]=min(v[i][j],min(v[i-k+1][j-k+1],min(mn[now][j-1][k-1],mn[now^1][j][k-1])));
38             }
39             if (min(i,j)>=n) ans=min(ans,mx[now][j][n]-mn[now][j][n]);
40         }
41     }
42     printf("%d\n",ans);
43     return 0;
44 }

(80分TLE)

  看了下题解:单调队列!

  豁然开朗,这不就是一个二维的滑动窗口吗?我个sb没想到啊……

  每行先做一遍单调队列优化DP,求出mx[i][j]表示以(i,j)为右端点的横着的n个格子的最大值(mn[i][j]同理)

  然后再对每列做一遍……(这时候每一格的值就代表了横着的n个格子的最优值)

  在对列进行DP的时候,为了节省(tou)空间(lan)我做完一列的DP就更新了一下答案……

P.S.感觉这个做法就是把一个二维的DP拆成(n+n)个一维DP分别搞……因为一维DP好搞、好优化= =所以总体上复杂度是降低了……(二维是n*n个状态,k的转移,一维是n个状态,转移可以优化到O(1),所以虽然一维DP要做2n遍,但总复杂度是$2×n^2$,更优)

 1 /**************************************************************
 2     Problem: 1047
 3     User: Tunix
 4     Language: C++
 5     Result: Accepted
 6     Time:1980 ms
 7     Memory:13240 kb
 8 ****************************************************************/
 9
10 //BZOJ 1047
11 #include<vector>
12 #include<cstdio>
13 #include<cstring>
14 #include<cstdlib>
15 #include<iostream>
16 #include<algorithm>
17 #define rep(i,n) for(int i=0;i<n;++i)
18 #define F(i,j,n) for(int i=j;i<=n;++i)
19 #define D(i,j,n) for(int i=j;i>=n;--i)
20 #define pb push_back
21 using namespace std;
22 inline int getint(){
23     int v=0,sign=1; char ch=getchar();
24     while(ch<‘0‘||ch>‘9‘){ if (ch==‘-‘) sign=-1; ch=getchar();}
25     while(ch>=‘0‘&&ch<=‘9‘){ v=v*10+ch-‘0‘; ch=getchar();}
26     return v*sign;
27 }
28 const int N=1010,M=110,INF=~0u>>2;
29 typedef long long LL;
30 /******************tamplate*********************/
31 int a,b,n,ans=INF;
32 int mx[N][N],mn[N][N],Q[N],t1[N],t2[N],v[N][N];
33 void get_row(){
34     int l=0,r=-1;
35     F(i,1,a){
36         l=0,r=-1;
37         F(j,1,b){
38             while(l<=r && v[i][Q[r]]<=v[i][j])r--;
39             Q[++r]=j;
40             while(l<=r && j-Q[l]>=n) l++;
41             if (j>=n) mx[i][j]=v[i][Q[l]];
42         }
43         l=0,r=-1;
44         F(j,1,b){
45             while(l<=r && v[i][Q[r]]>=v[i][j])r--;
46             Q[++r]=j;
47             while(l<=r && j-Q[l]>=n) l++;
48             if (j>=n) mn[i][j]=v[i][Q[l]];
49         }
50     }
51 }
52 void get_line(){
53     int l,r;
54     F(j,n,b){
55         l=0,r=-1;
56         F(i,1,a){
57             while(l<=r && mx[Q[r]][j]<=mx[i][j]) r--;
58             Q[++r]=i;
59             while(l<=r && i-Q[l]>=n) l++;
60             if (i>=n) t1[i]=mx[Q[l]][j];
61         }
62         l=0,r=-1;
63         F(i,1,a){
64             while(l<=r && mn[Q[r]][j]>=mn[i][j]) r--;
65             Q[++r]=i;
66             while(l<=r && i-Q[l]>=n) l++;
67             if (i>=n) t2[i]=mn[Q[l]][j];
68         }
69         F(i,n,a) ans=min(ans,t1[i]-t2[i]);
70     }
71 }
72 int main(){
73 #ifndef ONLINE_JUDGE
74     freopen("1047.in","r",stdin);
75     freopen("1047.out","w",stdout);
76 #endif
77     a=getint(); b=getint(); n=getint();
78     F(i,1,a) F(j,1,b) v[i][j]=getint();
79     get_row();
80     get_line();
81     printf("%d\n",ans);
82     return 0;
83 }

(正解)

时间: 2024-08-15 12:27:00

【BZOJ】【1047】【HAOI2007】理想的正方形的相关文章

BZOJ 1047 [HAOI2007]理想的正方形

[看了题解才会做 这是一个典型的二维滑动窗口问题~ 二维滑动窗口问题的处理方法可以类比一维滑动窗口的处理方法 首先将矩阵分解成B个列,对这B个列分别用单调队列维护一个长度为N的一维滑动窗口.记录colmax[i][j]为第j列第i行的元素(即第j个列的第i个元素)到第j列第i-N+1的元素(即第j个列的第i-N+1个元素)的最大值.这个用单调队列随便维护一下就好啦~ 然后将矩阵分解成A个行,对这A个行再分别用单调队列维护一个长度为N的一维滑动窗口.记录rowmax[i][j]为colmax[i]

BZOJ 1047 HAOI2007 理想的正方形 单调队列

题目大意:给定一个a*b的矩阵,求一个n*n的子矩阵,使矩阵中的最大值与最小值之差最小 对于每行维护一个单调递减的队列.再弄一个竖着的队列.维护n个格子之内的最大值就可以 两遍统计出最大值和最小值 然后得到ans就可以 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 1010 using namespace std; struct abcd

bzoj 1047: [HAOI2007]理想的正方形【单调队列】

没有复杂结构甚至不长但是写起来就很想死的代码类型 原理非常简单,就是用先用单调队列处理出mn1[i][j]表示i行的j到j+k-1列的最小值,mx1[i][j]表示i行的j到j+k-1列的最大值 然后就变成求单列最大最小值,用上面同样的方法处理出对于列的mn2mx2即可 #include<iostream> #include<cstdio> using namespace std; const int N=1005; int n,m,k,a[N][N],mn1[N][N],mx1[

【BZOJ】1047: [HAOI2007]理想的正方形(单调队列/~二维rmq+树状数组套树状数组)

http://www.lydsy.com/JudgeOnline/problem.php?id=1047 树状数组套树状数组真心没用QAQ....首先它不能修改..而不修改的可以用单调队列做掉,而且更快,只有O(n^2).而这货是n^2log^2n的建树...虽然查询是log^2n...但是建树那里就tle了.. 那么说题解... 先orz下,好神.. 我怎么没想到单调队列orz 首先我们维护 行 的单调队列,更新每个点在 列 距离内的最小and最大的值 然后我们维护 列 的单调队列,更新每个点

1047: [HAOI2007]理想的正方形——二维单调队列

http://www.lydsy.com/JudgeOnline/problem.php?id=1047 对每一行维护一个单调队列,保存在lmin[][],lmax[][] 然后对每一列维护一个单调队列,最后n*n枚举 #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #define clr(a,b) memset(a,b,sizeof(a)) cons

1047: [HAOI2007]理想的正方形

Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4075  Solved: 2277[Submit][Status][Discuss] Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. Input 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔.100%的数据2<=a,b<=

【BZOJ 1047】 [HAOI2007]理想的正方形

1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 1685  Solved: 891 [Submit][Status] Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. Input 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔.

[BZOJ1047][HAOI2007]理想的正方形

1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 3481  Solved: 1917 [Submit][Status][Discuss] Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值 的差最小. Input 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每 行相邻

RAM——[HAOI2007]理想的正方形

题目:[HAOI2007]理想的正方形 描述: [问题描述] 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小. [输入]: 第一行为3个整数,分别表示a,b,n的值 第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每行相邻两数之间用一空格分隔. [输出]: 仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值. [输入样例] 5 4 2 1 2 5 6 0 17 16 0 16 17

HAOI2007 理想的正方形 单调队列

单调队列 by   GeneralLiu 滑动窗口是比较裸的单调队列 理想的正方形   就拔高了一个层次(多了一维) 有一个a*b的整数组成的矩阵 现请你从中找出一个n*n的正方形区域 使得该区域所有数中的最大值和最小值的差最小 只写MAX了,MIN一个道理,懒 不写了 先横着跑单调队列 维护许多长度为 n 的 横着的MAX 存在数组 map1[][] 中 再对数组 map1[][] 竖着跑单调队列 就维护了 许多 n*n 的 矩阵的MAX MIN同理 在竖着跑单调队列时 顺便MAX 与 MIN