Нехай — вхідна прямокутна матриця. Створимо двовимірний масив такого ж розміру, де dp[i][j] — це сума елементів у підматриці з протилежними вершинами в точках та . Значення в цьому масиві можна обчислити за наступною формулою:
Розглянемо підматрицю масиву розміром w \times h. Нехай її протилежні кути знаходяться в точках з координатами та (i + w - 1, j + h - 1).
Тоді сума всіх чисел у підматриці обчислюється за формулою:
Розв'язуємо задачу повним перебором. Перебираємо всі можливі розміри підматриці w \times h, а також усі допустимі координати лівого верхнього кута цієї підматриці. За допомогою масиву за постійний час визначаємо суму чисел всередині поточної підматриці. Якщо ця сума не перевищує , то серед усіх таких підматриць запам'ятовуємо максимальну площу.
Однак такий перебір працює за O(n^4), що надто довго. Ми можемо прискорити його до O(n^3logn) за допомогою бінарного пошуку по ширині підматриці.
Зафіксуємо лівий верхній кут . Позначимо через g(w, h) суму елементів підматриці з вершинами та (i + w - 1, j + h - 1). Якщо зафіксувати висоту , а ширину розглядати як змінну, то функція g(w, h) буде неспадною по , так як вихідна матриця містить невід'ємні цілі числа. Це дозволяє застосувати бінарний пошук для знаходження максимального значення , при якому g(w, h) \le k.
Приклад
Наприклад, сума чисел у підматриці з кутами в точках та дорівнює
Реалізація алгоритму
Вхідну матрицю зберігаємо в масиві .
#define MAX 210
int a[MAX][MAX], dp[MAX][MAX];Функція f(w, h) повертає , якщо існує підматриця розміру w \times h, сума елементів у якій не перевищує . Для цього перебираються всі можливі координати лівого верхнього кута , і за допомогою масиву за константний час обчислюється сума елементів у прямокутнику з вершинами та (i + w - 1, j + h - 1).
int f(int w, int h)
{
for (int i = 1; i + w - 1 <= n; i ++)
for (int j = 1; j + h - 1 <= m; j ++)
if (dp[i + w - 1][j + h - 1] - dp[i + w - 1][j - 1] –
dp[i - 1][j + h - 1] + dp[i - 1][j - 1] <= k) return 1;
return 0;
};Основна частина програми. Читаємо вхідну матрицю.
scanf("%d %d %d",&n,&m,&k);
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++)
scanf("%d",&a[i][j]);На основі вхідної матриці обчислюємо допоміжну матрицю .
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++)
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + a[i][j];Перебираємо можливі значення ширини підматриці .
answer = 0;
for (w = 1; w <= n; w++)
{Довжину підматриці перебираємо за допомогою бінарного пошуку на відрізку [l; r].
l = 1; r = m;
while (l < r)
{
mid = (l + r) / 2;Для підматриці розміром w \times mid перебираємо всі можливі позиції її лівого верхнього кута . Якщо існує хоча б одне положення, при якому сума елементів у підматриці не перевищує , оновлюємо значення результуючої площі .
if (f(w,mid))
{
answer = max(answer, w * mid);
l = mid + 1;
}
else r = mid;
}
mid = (l + r) / 2;
if (f(w,mid))
answer = max(answer, w * mid);
};Виводимо площу знайденої максимальної підматриці.
printf("%d\n",answer);