题目描述
给你一个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)^2log 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 " , ans); return 0; }