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+k−1,j+k−1 的最大值。
数据范围
- 对于30% 的数据,n≤30;
- 对于 60% 的数据,n*≤300;
- 对于 100% 的数据,1≤k≤n≤2000。
- 1 ≤ a i , j ≤ 1 , 000 , 000 , 000 1\leq a_{i,j}\leq 1,000,000,000 1≤ai,j≤1,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");
}
}