📜  门|门 CS 1997 |第 72 题(1)

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

'门|门 CS 1997 |第 72 题'题解

题目描述

给定一个 $n \times m$ 的矩阵 $a_{i, j}$,你需要求出所有左右相邻元素相等的 $4$ 元组 $(i, j, i^\prime, j^\prime)$ 的个数,即满足 $a_{i, j} = a_{i, j+1}, a_{i, j} = a_{i^\prime, j^\prime}, a_{i^\prime, j^\prime} = a_{i^\prime, j^\prime+1}$ 且 $j < j^\prime$ 的 $(i, j, i^\prime, j^\prime)$ 的数量。

$$1 \leq n,m \leq 1000$$

思路

这是一道比较经典的矩阵问题,可以使用枚举和哈希表来解决。

枚举所有可能的 $4$ 元组 $(i, j, i^\prime, j^\prime)$,判断其是否满足题目条件。

而判断条件中包含元素相等,我们可以使用哈希表来判断。

具体来说,哈希表中的 key 为矩阵中出现过的元素值,value 为所有出现该元素值的位置。

然后遍历所有 $4$ 元组 $(i, j, i^\prime, j^\prime)$,判断 $a_{i, j} = a_{i, j+1}$ 和 $a_{i^\prime, j^\prime} = a_{i^\prime, j^\prime+1}$ 是否成立,并从哈希表中查询是否有 $a_{i, j}$ 和 $a_{i^\prime, j^\prime}$ 的索引值在 $j+1$ 和 $j^\prime$ 之间,并统计满足条件的 $4$ 元组数量即可。

这样做的时间复杂度为 $O(nm^2+C)$,其中 $C$ 为哈希表操作的次数。由于 $C$ 的大小与矩阵中元素有关,故该方法可能存在一定的数据敏感性。

代码

下面是基于哈希表的实现,使用 C++ 编写:

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

const int MAXN = 1e3+5;

int n, m, a[MAXN][MAXN];
unordered_map<int, vector<int>> mp;

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
            mp[a[i][j]].push_back((i-1)*m+j); // 标记相同元素的位置索引
        }
    }
    int ans = 0;
    for (int j = 1; j <= m-1; j++) { // 枚举所有可能的 4 元组
        for (int i = 1; i <= n; i++) {
            if (a[i][j] == a[i][j+1]) {
                int x = a[i][j]; // 取出相同元素值
                int l = lower_bound(mp[x].begin(), mp[x].end(), (i-1)*m+j+1) - mp[x].begin(); // 查询左边相邻元素位置的索引
                int r = upper_bound(mp[x].begin(), mp[x].end(), (i-1)*m+j+1) - mp[x].begin() - 1; // 查询右边相邻元素位置的索引
                for (int k = l; k <= r; k++) {
                    int x1 = (mp[x][k]-1) / m + 1, y1 = (mp[x][k]-1) % m + 1, y2 = j+1; // 将位置索引转换为坐标
                    if (x1 > i) ans++; // 判断位置在右下方的 4 元组是否满足条件
                }
            }
        }
    }
    cout << ans << endl;
    return 0;
}

该代码片段返回的 markdown 内容为:

# '门|门 CS 1997 |第 72 题'题解

## 题目描述

给定一个 $n \times m$ 的矩阵 $a_{i, j}$,你需要求出所有左右相邻元素相等的 $4$ 元组 $(i, j, i^\prime, j^\prime)$ 的个数,即满足 $a_{i, j} = a_{i, j+1}, a_{i, j} = a_{i^\prime, j^\prime}, a_{i^\prime, j^\prime} = a_{i^\prime, j^\prime+1}$ 且 $j < j^\prime$ 的 $(i, j, i^\prime, j^\prime)$ 的数量。

$$1 \leq n,m \leq 1000$$

## 思路

这是一道比较经典的矩阵问题,可以使用枚举和哈希表来解决。

枚举所有可能的 $4$ 元组 $(i, j, i^\prime, j^\prime)$,判断其是否满足题目条件。

而判断条件中包含元素相等,我们可以使用哈希表来判断。

具体来说,哈希表中的 key 为矩阵中出现过的元素值,value 为所有出现该元素值的位置。

然后遍历所有 $4$ 元组 $(i, j, i^\prime, j^\prime)$,判断 $a_{i, j} = a_{i, j+1}$ 和 $a_{i^\prime, j^\prime} = a_{i^\prime, j^\prime+1}$ 是否成立,并从哈希表中查询是否有 $a_{i, j}$ 和 $a_{i^\prime, j^\prime}$ 的索引值在 $j+1$ 和 $j^\prime$ 之间,并统计满足条件的 $4$ 元组数量即可。

这样做的时间复杂度为 $O(nm^2+C)$,其中 $C$ 为哈希表操作的次数。由于 $C$ 的大小与矩阵中元素有关,故该方法可能存在一定的数据敏感性。

## 代码

下面是基于哈希表的实现,使用 C++ 编写:

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

const int MAXN = 1e3+5;

int n, m, a[MAXN][MAXN];
unordered_map<int, vector<int>> mp;

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
            mp[a[i][j]].push_back((i-1)*m+j); // 标记相同元素的位置索引
        }
    }
    int ans = 0;
    for (int j = 1; j <= m-1; j++) { // 枚举所有可能的 4 元组
        for (int i = 1; i <= n; i++) {
            if (a[i][j] == a[i][j+1]) {
                int x = a[i][j]; // 取出相同元素值
                int l = lower_bound(mp[x].begin(), mp[x].end(), (i-1)*m+j+1) - mp[x].begin(); // 查询左边相邻元素位置的索引
                int r = upper_bound(mp[x].begin(), mp[x].end(), (i-1)*m+j+1) - mp[x].begin() - 1; // 查询右边相邻元素位置的索引
                for (int k = l; k <= r; k++) {
                    int x1 = (mp[x][k]-1) / m + 1, y1 = (mp[x][k]-1) % m + 1, y2 = j+1; // 将位置索引转换为坐标
                    if (x1 > i) ans++; // 判断位置在右下方的 4 元组是否满足条件
                }
            }
        }
    }
    cout << ans << endl;
    return 0;
}

该代码片段中涉及到的技巧有:

  • 二维矩阵可以通过一维数组来表示。
  • 判断 $4$ 元组是否满足条件时,可以先判断左边两个元素是否相等,再从哈希表中查询右边两个元素是否相等。
  • 将位置索引转换为坐标时,需要注意两个索引的计算方式不同。