Шахівниця#13093

Шахівниця (Розбір)

Ідея

Потрібно видалити рівно один рядок та один стовпець таблиці, після чого змінити якомога менше клітинок, щоб отримати шахове розфарбування з нулем у лівому верхньому куті.

Зауважимо, що потрібна таблиця визначається однозначно: у клітинці повинно стояти число (i+j)\bmod 2, якщо індексація починається з .

Найпростіше рішення — перебрати всі можливі пари рядка та стовпця, які будемо видаляти, і для кожної з них порахувати кількість неправильних клітинок у таблиці, що залишилася.

Однак існує можливих пар, а перевірка кожної потребує часу. Загальна складність становитиме O(n^2m^2), що занадто повільно.

Ключове спостереження

Спочатку порівняємо кожну клітинку початкової таблиці з відповідною клітинкою шахового розфарбування.

Визначимо

b_{i,j}=a_{i,j}\oplus((i+j)\bmod 2).

Тут позначає побітову операцію XOR.

Таким чином, b_{i,j}=1, якщо клітинка відрізняється від потрібного шахового розфарбування, і b_{i,j}=0 в іншому випадку.

Тепер нехай ми видаляємо рядок та стовпець .

Усі клітинки, що залишилися, можна розділити на чотири області відносно видаленого рядка та стовпця.

Отже, у лівій верхній та правій нижній областях потрібно змінити всі клітинки, для яких b_{i,j}=1.

У двох інших областях потрібно змінити клітинки, для яких b_{i,j}=0.

Видалені рядок та стовпець не належать жодній із цих областей.

Розв'язок

Можемо обчислювати кількість необхідних змін для кожної пари за допомогою двовимірних префіксних сум.

Нехай p_{i,j} — кількість одиниць у прямокутнику від до у перетвореній таблиці .

Тоді

p_{i,j}=b_{i,j}+p_{i-1,j}+p_{i,j-1}-p_{i-1,j-1}.

За допомогою цих префіксних сум можна знайти кількість одиниць у будь-якому прямокутнику за .

Для фіксованого видаленого рядка та стовпця позначимо кількості одиниць у чотирьох областях через S_{TL}, S_{TR}, S_{BL} та S_{BR}.

Права верхня область містить (r-1)(m-c) клітинок, а ліва нижня — (n-r)(c-1) клітинок.

Оскільки у цих двох областях потрібно порахувати кількість нулів, загальна кількість необхідних змін дорівнює

\begin{aligned} cost(r,c)={}&S_{TL}+S_{BR}\\ &+(r-1)(m-c)-S_{TR}\\ &+(n-r)(c-1)-S_{BL}. \end{aligned}

Кожну із чотирьох сум можна отримати за за допомогою префіксних сум.

Таким чином, достатньо перебрати всі можливі рядки та стовпці для видалення, обчислити cost(r,c) і вибрати мінімальне значення.

Відповідь дорівнює

\boxed{\min_{1\leq r\leq n,\ 1\leq c\leq m}cost(r,c)}.

Асимптотика

Побудова двовимірних префіксних сум потребує часу.

Існує можливих пар видаленого рядка та стовпця, кожну з яких ми перевіряємо за .

Отже, загальна часова складність становить .

Для зберігання таблиці префіксних сум потрібно додаткової пам'яті.

Реалізація

#include <bits/stdc++.h>
using namespace std;

void solve() {
    int n, m;
    cin >> n >> m;

    vector<vector<int>> p(n + 1, vector<int>(m + 1, 0));

    for (int i = 1; i <= n; i++) {
        string s;
        cin >> s;

        for (int j = 1; j <= m; j++) {
            int x = (s[j - 1] - '0') ^ ((i + j) & 1);

            p[i][j] = x + p[i - 1][j] + p[i][j - 1]
                        - p[i - 1][j - 1];
        }
    }

    int ans = n * m;

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            int tl = p[i - 1][j - 1];

            int tr = p[i - 1][m] - p[i - 1][j];

            int bl = p[n][j - 1] - p[i][j - 1];

            int br = p[n][m] - p[i][m]
                   - p[n][j] + p[i][j];

            int cost = tl + br;

            cost += (i - 1) * (m - j) - tr;
            cost += (n - i) * (j - 1) - bl;

            ans = min(ans, cost);
        }
    }

    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    solve();

    return 0;
}