题目描述
给你一个n×m的矩形,要你找一个子矩形,价值为左上角左下角右上角右下角这四个数的最小值,要你最大化矩形的价值。
输入
第一行两个数n,m,接下来n行每行m个数,用来描述矩形
n, m ≤ 1000
输出
输出一个数表示答案
样例输入
2 2
1 2
3 4
样例输出
1
题解
二分+暴力
首先题目问的是最小值最大,显然二分答案,问题转化为判断是否存在一个子矩形,满足四个角的权值大于等于mid。
考虑暴力怎么做:枚举所有与x轴平行的线段(即同行的线段),判断是否有两个线段的端点横坐标相同(即列数相同)。
由于只要有任何一种线段出现次数大于等于2即可行,因此最坏情况下每一种线段也只出现了一次。
因为线段只有 $m^2$ 个,所以只需要使用严格的复杂度把所有线段暴力找出来即可。这样最坏情况下时间复杂度不会超过 $O(nm+m^2)$
因此总的时间复杂度为 $O((n+m)^2\log n)$
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1010
using namespace std;
int n , m , a[N][N] , c[N][N] , v[N] , tot;
bool solve(int mid)
{
int i , j , k;
for(i = 1 ; i <= m ; i ++ )
for(j = i + 1 ; j <= m ; j ++ )
c[i][j] = 0;
for(i = 1 ; i <= n ; i ++ )
{
tot = 0;
for(j = 1 ; j <= m ; j ++ )
if(a[i][j] >= mid)
v[++tot] = j;
for(j = 1 ; j <= tot ; j ++ )
{
for(k = j + 1 ; k <= tot ; k ++ )
{
if(c[v[j]][v[k]]) return 1;
c[v[j]][v[k]] = 1;
}
}
}
return 0;
}
int main()
{
int i , j , l = 1 << 30 , r = 0 , mid , ans;
scanf("%d%d" , &n , &m);
for(i = 1 ; i <= n ; i ++ )
for(j = 1 ; j <= m ; j ++ )
scanf("%d" , &a[i][j]) , l = min(l , a[i][j]) , r = max(r , a[i][j]);
while(l <= r)
{
mid = (l + r) >> 1;
if(solve(mid)) ans = mid , l = mid + 1;
else r = mid - 1;
}
printf("%d\n" , ans);
return 0;
}