单调队列

bzoj1047 理想的正方形

题目大意:求a*b的矩阵中一个n*n的子矩阵,使得子矩阵的最大值和最小值的差最小。

思路:一开始认为能用二维线段树a掉,但lcomyn大神写了一下,结果T了,于是就寻找新的写法。借鉴了斜率优化的思路,发现单调队列可以优越的做到O(ab)的求出整个矩阵中每个点左面延伸n位的最值。我们用行上的单调队列维护出这个之后,再从列上单调队列一下,就能求出一个n*n子矩阵的最值了,然后比较一下,输出答案。注意这里单调队列中的是下标(这样写可能会有一些好处),所以写的时候要对应到数组中。

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxnode 1010
using namespace std;
int q1[maxnode]={0},q2[maxnode]={0},map[maxnode][maxnode]={0},h1,h2,t1,t2,n,
    maxn[maxnode][maxnode]={0},minn[maxnode][maxnode]={0};
void qmin(int i,int j)
{
    while(h1<=t1&&q1[h1]<=j-n) ++h1;
    while(h1<=t1&&map[i][q1[t1]]>=map[i][j]) --t1;
    q1[++t1]=j;
}
void qmax(int i,int j)
{
    while(h2<=t2&&q2[h2]<=j-n) ++h2;
    while(h2<=t2&&map[i][q2[t2]]<=map[i][j]) --t2;
    q2[++t2]=j;
}
void wmin(int i,int j)
{
    while(h1<=t1&&q1[h1]<=i-n) ++h1;
    while(h1<=t1&&minn[q1[t1]][j]>=minn[i][j]) --t1;
    q1[++t1]=i;
}
void wmax(int i,int j)
{
    while(h2<=t2&&q2[h2]<=i-n) ++h2;
    while(h2<=t2&&maxn[q2[t2]][j]<=maxn[i][j]) --t2;
    q2[++t2]=i;
}
int main()
{
    int i,j,a,b,ans;
    scanf("%d%d%d",&a,&b,&n);
    for (i=1;i<=a;++i)
      for (j=1;j<=b;++j)
        scanf("%d",&map[i][j]);
    /*求点(i,j)左面n内的最大最小值*/
    for (i=1;i<=a;++i)
    {
      h1=h2=1;t1=t2=0;
      for (j=1;j<=b;++j)
      {
        qmin(i,j);qmax(i,j);
        if (j>=n)
        {
            minn[i][j]=map[i][q1[h1]];maxn[i][j]=map[i][q2[h2]];
        }
      }
    }
    /*求列j为右边界的矩形的ans*/
    ans=0x7fffffff;
    for (j=1;j<=b;++j)
    {
        h1=h2=1;t1=t2=0;
        for (i=1;i<=a;++i)
        {
            wmin(i,j);wmax(i,j);
            if (j>=n&&i>=n&&maxn[q2[h2]][j]-minn[q1[h1]][j]<ans) ans=maxn[q2[h2]][j]-minn[q1[h1]][j];
        }
    }
    printf("%d\n",ans);
}
View Code

 

xjoi T2

题目大意:给定一个nm的矩阵,求最大最小值差不超过k的子矩阵的个数。

思路:穷举i~j行,然后扫出每一列的最大最小值,用单调队列维护这些最大最小值,插入的时候就是从队尾比较着插入,但是查询的时候还要注意k的限制,所以先把队首那些不满足的弹出(但是要注意:如果这次没有弹出队首,符合的区间可能是上次更新时的位置,而不是0),然后累加答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxm 405
#define LL long long
using namespace std;
int ai[maxm][maxm],q1[maxm],q2[maxm],maxn[maxm],minn[maxm];
int main(){
    int n,m,k,i,j,a,b,h1,h2,t1,t2;LL ans=0;
    scanf("%d%d%d",&n,&m,&k);
    for (i=1;i<=n;++i)
      for (j=1;j<=m;++j) scanf("%d",&ai[i][j]);
    for (i=1;i<=n;++i){
      memset(maxn,0,sizeof(maxn));
      memset(minn,127,sizeof(minn));
      for (j=i;j<=n;++j){
          h1=h2=1;t1=t2=0;
        for (b=a=1;a<=m;++a){
            maxn[a]=max(maxn[a],ai[j][a]);
            while(h1<=t1&&maxn[a]>=maxn[q1[t1]]) --t1;
            minn[a]=min(minn[a],ai[j][a]);
            while(h2<=t2&&minn[a]<=minn[q2[t2]]) --t2;
            q1[++t1]=q2[++t2]=a;
            while(h1<=t1&&maxn[q1[h1]]-k>minn[a]){b=max(b,q1[h1]+1);++h1;}
            while(h2<=t2&&minn[q2[h2]]+k<maxn[a]){b=max(b,q2[h2]+1);++h2;}
            ans+=(LL)(a-b+1);
        }
      }
    }cout<<ans<<endl;
}
View Code

 

posted @ 2015-07-04 18:55  Rivendell  阅读(256)  评论(0编辑  收藏  举报