码迷,mamicode.com
首页 > 其他好文 > 详细

P2216 [HAOI2007]理想的正方形

时间:2018-02-10 11:11:47      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:printf   ret   turn   ++   names   txt   a*   pen   维护   

题目描述

有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

输入输出格式

输入格式:

 

第一行为3个整数,分别表示a,b,n的值

第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。

 

输出格式:

 

仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。

 

输入输出样例

输入样例#1: 复制
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
输出样例#1: 复制
1

说明

问题规模

(1)矩阵中的所有数都不超过1,000,000,000

(2)20%的数据2<=a,b<=100,n<=a,n<=b,n<=10

(3)100%的数据2<=a,b<=1000,n<=a,n<=b,n<=100

 

ST表属于区间dp吧

 

//正常纯矩阵ST表

#include<bits/stdc++.h>
using namespace std;
#define maxn 1001
typedef long long ll;
#define inf 0x3fffffff

int mi[maxn][maxn][11],ma[maxn][maxn][11];
int n,m,k;
int l,p;

inline char getc(void)
{
    static char buf[1 << 18], *fs, *ft;
    return (fs == ft && (ft = (fs = buf) + fread(buf, 1, 1 << 18, stdin)), fs == ft) ? EOF : *fs++;
}

inline int read(void)
{
    char tmp = getc();
    int res = 0;
    for(; !isdigit(tmp); tmp = getc());
    for(; isdigit(tmp); tmp = getc())
        res = ((res + (res << 2)) << 1) + (tmp ^ 0x30);
    return res;
}

int Work(int x,int y)
{
    int x1=x+k-1,y1=y+k-1;
    int MIN=min(
                min(mi[x][y][p],mi[x][y1-(1<<p)+1][p]),
                min(mi[x1-(1<<p)+1][y][p],mi[x1-(1<<p)+1][y1-(1<<p)+1][p])
            );
    int MAX=max(
                max(ma[x][y][p],ma[x][y1-(1<<p)+1][p]),
                max(ma[x1-(1<<p)+1][y][p],ma[x1-(1<<p)+1][y1-(1<<p)+1][p])
            );
    return MAX-MIN;
}

int main()
{
//    freopen("test.txt","r",stdin);
    memset(mi,0x3f,sizeof(mi));
    memset(ma,0,sizeof(ma));
    n=read(),m=read(),k=read();
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            mi[i][j][0]=ma[i][j][0]=read();
    l=log2(min(n,m)),p=log2(k);
    for(int k=1; k<=l; k++)
    {
        int x=1<<(k-1);
        for(int i=1; i<=n-x; i++)
            for(int j=1; j<=m-x; j++)
            {
                mi[i][j][k]=min(
                                min(mi[i][j][k-1],mi[i+x][j+x][k-1]),
                                min(mi[i][j+x][k-1],mi[i+x][j][k-1])
                            );
                ma[i][j][k]=max(
                                max(ma[i][j][k-1],ma[i+x][j][k-1]),
                                max(ma[i][j+x][k-1],ma[i+x][j+x][k-1])
                            );
            }
    }
    int ans=inf;
    for(int i=1; i<=n-k+1; i++)
        for(int j=1; j<=m-k+1; j++)
            ans=min(ans,Work(i,j));
    cout<<ans;

    return 0;
}

 

//单调队列+ST表

#include<cstdio>
#include<iostream>
#include<cmath>
#define For(i,x,y) for (int i=x;i<=y;i++)
#define N 1010
#define inf 1<<30
using namespace std;
int a[N][N],ma[N][N][11],mi[N][N][11],qmi[N][2],qma[N][2];
int n,m,t,h1,t1,h2,t2,ans,x;
inline int read()
{
    int x=0;
    int ch=getchar(),f=1;
    while (!isdigit(ch)&&(ch!=-)&&(ch!=EOF)) ch=getchar();
    if (ch==-)
    {
        f=-1;
        ch=getchar();
    }
    while (isdigit(ch))
    {
        x=(x<<1)+(x<<3)+ch-0;
        ch=getchar();
    }
    return x*f;
} //读优
int queryma(int k,int x,int y)
{
    int l=log2(y-x+1);
    return max(ma[k][x][l],ma[k][y-(1<<l)+1][l]);
} //查询第k行的第x列到第y列的最大值
int querymi(int k,int x,int y)
{
    int l=log2(y-x+1);
    return min(mi[k][x][l],mi[k][y-(1<<l)+1][l]);
} //查询第k行的第x列到第y列的最小值
int main()
{
    n=read(),m=read(),t=read();
    int l=log2(max(n,m));
    For(i,1,n)
    For(j,1,m)
    {
        a[i][j]=read();
        ma[i][j][0]=mi[i][j][0]=a[i][j];
        For(k,1,10)
        mi[i][j][k]=inf;
    }
    For(i,1,n)
    For(k,1,l)
    {
        x=1<<(k-1);
        For(j,1,m-x)
        {
            ma[i][j][k]=max(ma[i][j][k-1],ma[i][j+x][k-1]);
            mi[i][j][k]=min(mi[i][j][k-1],mi[i][j+x][k-1]);
        }
    } //求ma和mi数组
    ans=inf;
    For(i,1,m)
    {
        if (i+t-1>m) break;
        h1=h2=1,t1=t2=0;
        For(j,1,n)
        {
            x=queryma(j,i,i+t-1);
            while (x>=qma[t1][1]&&h1<=t1) t1--;
            qma[++t1][1]=x;
            qma[t1][0]=j;
            x=querymi(j,i,i+t-1);
            while (x<=qmi[t2][1]&&h2<=t2) t2--;
            qmi[++t2][1]=x;
            qmi[t2][0]=j;//两个单调队列维护最大值和最小值
            if (j>=t)
            {
                while (j-t>=qma[h1][0]) h1++;
                while (j-t>=qmi[h2][0]) h2++;
                if (qma[h1][1]-qmi[h2][1]<ans) ans=qma[h1][1]-qmi[h2][1];
            }
        }
    }
    printf("%d",ans);
    return 0;
}

 

P2216 [HAOI2007]理想的正方形

标签:printf   ret   turn   ++   names   txt   a*   pen   维护   

原文地址:https://www.cnblogs.com/planche/p/8438066.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!