📌  相关文章
📜  查询以计数仅用0填充二进制子矩阵所需的最小翻转(1)

📅  最后修改于: 2023-12-03 15:10:47.816000             🧑  作者: Mango

问题描述

给定一个 $n \times m$ 的二进制矩阵,现在你需要查询一个子矩阵并计算出将其中所有的 $1$ 变为 $0$、$0$ 变为 $1$ 所需的最小步数,使得矩阵中只剩下 $0$。

输入格式

第一行包含三个整数 $n,m,q$,表示矩阵的行数、列数及查询次数。

接下来 $n$ 行,每行包含 $m$ 个整数 $0/1$,表示矩阵中的元素。

接下来 $q$ 行,每行包含四个整数 $x_1,y_1,x_2,y_2$,表示一个查询的子矩阵的左上角和右下角坐标。

其中 $1\leq n,m,q\leq 1000$。

输出格式

对于每个查询,输出一个整数表示将矩阵中所有元素变为 $0$ 所需的最小操作次数。

数据范围

输入样例1:

3 3 2
0 1 0
1 0 1
1 0 1
1 1 3 2
2 2 3 3

输出样例1:

5
1

输入样例2:

4 4 4
0 0 1 1
1 0 0 1
0 1 0 0
1 0 1 1
1 1 1 2
1 1 4 4
1 1 2 2
3 3 4 4

输出样例2:

6
3
3
2
题解

以下给出解题思路,需要注意的是下标从 $0$ 起始。

题目要求将子矩阵中所有的 $0/1$ 反转,并使得矩阵中只剩下 $0$。换而言之,我们需要把之前的 $0/1$ 都转为 $1/0$,最后再将所有的 $1$ 转为 $0$。具体做法如下:

  • 对于每个子矩阵,先分别统计子矩阵中的 $1/0$ 的个数.

  • 计算一下当前子矩阵中需要把 $1$ 变为 $0$ 的最小次数 $cnt_1$ 以及需要把 $0$ 变为 $1$ 的最少次数 $cnt_0$。 设这个子矩阵中共有k个0元素,则将矩阵中所有元素变为0所需要的步骤:ans = min(cnt_0 * (n-k) + cnt_1 * k, cnt_1 * (n-k) + cnt_0 * k)。这个式子的意义是,将所有的 $0$ 变为 $1$ 或将所有的 $1$ 变为 $0$,它们的答案是相等的,因此只要比较一下这两者的代价即可。

  • 建立前缀和预处理并查询区间和,来统计子矩阵中 $1/0$ 的个数。

  • 根据上面的思路求出答案即可。

代码如下:

n, m, q = map(int, input().split())

# matrix 存储矩阵
matrix = [[0] * (m + 1) for _ in range(n + 1)]

# cnt0:子矩阵中0的个数 cnt1:子矩阵中1的个数
for i in range(1, n + 1):
    line = list(map(int, input().split()))
    for j in range(1, m + 1):
        matrix[i][j] = line[j - 1]

# 求前缀和,注意下标从1起始
s = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
    for j in range(1, m + 1):
        s[i][j] = s[i][j-1] + s[i-1][j] + matrix[i][j] - s[i-1][j-1]

for _ in range(q):
    x1, y1, x2, y2 = map(int, input().split())
    cnt0 = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]
    cnt1 = (x2-x1+1)*(y2-y1+1) - cnt0
    k = cnt0
    ans = min(cnt0*(n-k) + cnt1*k, cnt1*(n-k) + cnt0*k)
    print(ans)

时间复杂度分析:

本题的时间复杂度为 $O(n^2+q)$,其中 $q$ 为查询次数。通过矩阵前缀和,可以将求前缀和的时间复杂度优化至 $O(n^2)$,因此算法的瓶颈在于查询子矩阵中 $1/0$ 的个数,所以总的时间复杂度为 $O(n^2+q)$。