2022年7月上海月赛T3 方形最大值

T3方形最大值

内存限制: 256 Mb

时间限制: 1000 ms

题目描述

给定 n×n个整数 a i , j a_{i,j} ai,j 构成一个方阵,给定 k,请求出原方阵中每个 k × k k\times k k×k的小方阵的最大值分别是多少。

输入格式

第一行:两个整数 n与 k。
第二行到第 n+1行:第 i+1行有 n个整数表示 a i , 1 a_{i,1} ai,1 a i , n a_{i,n} ai,n​。

输出格式

共 n-k+1行:每行 n-k+1个数字,其中第 i 行 第 j 列的数字表示原方阵中 a i , j a_{i,j} ai,j a i + k − 1 , j + k − 1 a_{i+k-1,j+k-1} ai+k1,j+k1 的最大值。

数据范围
  • 对于30% 的数据,n≤30;
  • 对于 60% 的数据,n*≤300;
  • 对于 100% 的数据,1≤kn≤2000。
  • 1 ≤ a i , j ≤ 1 , 000 , 000 , 000 1\leq a_{i,j}\leq 1,000,000,000 1ai,j1,000,000,000
样例数据

输入:

5 3
1 2 3 4 5
1 2 3 4 5
5 4 3 2 1
5 4 3 2 1
1 1 1 1 1

输出:

5 4 5
5 4 5
5 4 3
思路一、暴力打表:
时间复杂度 $O(N^2 *K^2)$60 分
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 2010;
int n,k;
int a[N][N];

int max_item(int x, int y )
{
    int max_i = 0;
    for(int i = x;  i<= x+k-1 ;i ++)
    {
        for (int j = y; j <= y+k-1; j ++ )
        {
            max_i = max(max_i, a[i][j]);
        }
    }
    return max_i;
}

int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            scanf("%d", &a[i][j]);
    for (int i = 1; i <= n-k+1; i ++ )
    {
        for (int j = 1; j <= n-k+1; j ++ )
        {
            printf("%d ",max_item(i,j));
        }
        cout << endl;
    }
    
    return 0;
}
思路二、ST表法
时间复杂度 O ( N 2 l o g ( N 2 ) ) O(N^2 log(N^2)) O(N2log(N2)) 60 分
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

int a,b,n,k;
int sta[2022][2022][12],sti[2022][2022][12];
int kk;

inline int query1(int x,int y,int xx,int yy)
{
	int t1=sta[x][y][kk],t2=sta[xx-(1<<kk)+1][y][kk],t3=sta[x][yy-(1<<kk)+1][kk],t4=sta[xx-(1<<kk)+1][yy-(1<<kk)+1][kk];
	return max(max(max(t1,t2),t3),t4);
}
inline int query2(int x,int y,int xx,int yy)
{
	int t1=sti[x][y][kk],t2=sti[xx-(1<<kk)+1][y][kk],t3=sti[x][yy-(1<<kk)+1][kk],t4=sti[xx-(1<<kk)+1][yy-(1<<kk)+1][kk];
	return min(min(min(t1,t2),t3),t4);
}
inline int Max(int a,int b,int c,int d)
{
	return max(max(max(a,b),c),d);
}
inline int Min(int a,int b,int c,int d)
{
	return min(min(min(a,b),c),d);
}


int main()
{
    cin >> n >> k;
    a = n , b = n;
    
    for (int i=1;i<=a;++i) 
        for (int j=1;j<=b;++j) 
        {
            int tmp;
            scanf("%d",&tmp);
            sta[i][j][0]=sti[i][j][0]=tmp;
        }
     初始化 
    
    for (int k=1;k<=12;++k)
		for (int i=1;i+(1<<k)-1<=a;++i)
			for (int j=1;j+(1<<k)-1<=b;++j)
			{
				sta[i][j][k]=Max(sta[i][j][k-1],sta[i+(1<<k-1)][j][k-1],sta[i][j+(1<<k-1)][k-1],sta[i+(1<<k-1)][j+(1<<k-1)][k-1]);
				sti[i][j][k]=Min(sti[i][j][k-1],sti[i+(1<<k-1)][j][k-1],sti[i][j+(1<<k-1)][k-1],sti[i+(1<<k-1)][j+(1<<k-1)][k-1]);
			}
			
			
	 强制转化int		
    kk=(int)(log(k)/log(2)); 
    
    for (int i = 1; i <= n - k + 1; i ++ )
    {
        for (int j = 1; j <= n - k + 1; j ++ )
            printf("%d ", query1(i,j, i+k-1, j+k-1));
        printf("\n");
    }
    
    return 0;
}
思路三、二维单调队列法
时间复杂度:$O((N+K)^2)$100分
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 2020;

// a 是原数组, 
// b[i][j] 表示起点为a[i][j-k+1]到a[i][j] 最大值(横向)
// c[i][j] 表示起点为b[i-k+1][j]到b[i][j] 最大值(纵向)

int a[N][N], b[N][N], c[N][N];

struct node
{
    int pos, val; // pos 代表下标, val 代表值
};

inline void read(int &x) // 读数据需要用到快读。 
{
    x =0;
    char c = getchar();
    while(c<'0' || c>'9') c= getchar();
    while(c>='0' && c<='9')
    {
        x = (x<<3) + (x<<1) + (c^48);
        c = getchar();
    }
}

int main()
{
     输入数据 
    int n, k;
    read(n);
    read(k);
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            read(a[i][j]);
    
     (横向处理)单调队列处理b数组,
    
    for (int i = 1; i <= n; i ++ )
    {
        deque<node> q;
        for(int j = 1; j<=n; j++)
        {
            // 判断队头是否出队
            if(!q.empty() && q.front().pos<j - k + 1) q.pop_front();
            // 维护队列单调性
            while(!q.empty()&& a[i][j] >= q.back().val) q.pop_back();
            // 下标,值入队
            q.push_back({j,a[i][j]});
            // 取出队头作为窗口最大元素。 
            if(j >= k) b[i][j]=q.front().val;
        }
    }
    
     通过上面得到的b数组,(纵向处理)单调队列处理得到c数组。c是最终的数组 
    for (int j = 1; j <=n; j ++ )
    {
        deque<node> q;
        for (int i = 1; i <= n; i ++ )
        {
            // 判断队头是否出队
            if(!q.empty() && q.front().pos < i - k + 1) q.pop_front();
            // 维护队列单调性
            while(!q.empty() && b[i][j] >= q.back().val) q.pop_back();
            // 下标,值入队
            q.push_back({i, b[i][j]});
            // 取出对头作为窗口最大元素
            if(i>=k) c[i][j] = q.front().val;
        }
    }
    
     c数组输出数据 
    
    for(int i = k; i <= n; i++)
    {
        for (int j = k; j <=n; j ++ )
            printf("%d ", c[i][j]);
        printf("\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值