洛谷 P10379:[GESP202403 七级] 俄罗斯方块 ← 暴力搜索 + 字符串哈希

【题目来源】
https://www.luogu.com.cn/problem/P10379

【题目描述】
小杨同学用不同种类的俄罗斯方块填满了一个大小为 n×m 的网格图。
网格图由 n×m 个带颜色方块构成。小杨同学现在将这个网格图交给了你,请你计算出网格图中俄罗斯方块的种类数。
如果两个同色方块是四连通(即上下左右四个相邻的位置)的,则称两个同色方块直接连通;若两个同色方块同时与另一个同色方块直接或间接连通,则称两个同色方块间接连通。一个俄罗斯方块由一个方块和所有与其直接或间接连接的同色方块组成。定义两个俄罗斯方块的种类相同当且仅当通过平移其中一个俄罗斯方块可以和另一个俄罗斯方块重合;如果两个俄罗斯方块颜色不同,仍然视为同一种俄罗斯方块。
......

【输入格式】
第一行包含两个正整数 n 和 m,表示网格图的大小。
对于之后的 n 行,第 i 行包含 m 个正整数 a_i1,a_i2,...a_im,表示该行 m 个方块的颜色。

【输出格式】
输出一行一个整数表示答案。

【输入样例】
5 6
1 2 3 4 4 5
1 2 3 3 4 5
1 2 2 3 4 5
1 6 6 7 7 8
6 6 7 7 8 8

【输出样例】
7

【数据范围】
对全部的测试数据,保证 1≤n,m≤500,1≤a_ij≤n×m。

【算法分析】
● norm() 函数的功能,是将一组坐标点转换为一个唯一标识形状的字符串,用于判断两个连通块是否形状相同。核心代码分析如下:
(1)找到最小坐标:找到所有点中最小的行号和列号。

cpp 复制代码
int tx=v[0].first;
int ty=v[0].second;
for(auto x:v) {
    tx=min(tx,x.first);
    ty=min(ty,x.second);
}

(2)平移归一化:将所有坐标减去最小值,使形状平移到原点附近。

cpp 复制代码
s+=to_string(x.first-tx)+","+to_string(x.second-ty)+";";

示例说明:
假设有两个形状相同的连通块:
形状A:{(2,3), (2,4), (3,3)}
形状B:{(5,6), (5,7), (6,6)}
经过归一化后都变成:"0,0;0,1;1,0;"。
其中,逗号分隔同一坐标的行和列,分号分隔不同的坐标点。

● 为什么能去重?set<string> 会自动"递增无重",因为:
相同形状 → 归一化后的字符串完全相同
不同形状 → 归一化后的字符串不同
所以 st.insert(norm()) 只会保留不同形状的字符串表示,最终 st.size() 就是不同形状的数量。

【算法代码】

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N=5e2+5;
int a[N][N],g[N][N];
vector<pair<int,int>> v;
set<string> st;
int n,m;
int dx[]= {1,0,-1,0};
int dy[]= {0,1,0,-1};

void dfs(int x,int y,int val) {
    v.push_back({x,y});
    g[x][y]=1;
    for(int i=0; i<4; i++) {
        int tx=x+dx[i],ty=y+dy[i];
        if(tx>=1 && tx<=n && ty>=1 && ty<=m) {
            if(!g[tx][ty] && a[tx][ty]==val) dfs(tx,ty,val);
        }
    }
}

string norm() {
    int tx=v[0].first;
    int ty=v[0].second;
    for(auto x:v) {
        tx=min(tx,x.first);
        ty=min(ty,x.second);
    }
    string s;
    for(auto x:v) {
        s+=to_string(x.first-tx)+","+to_string(x.second-ty)+";";
    }
    return s;
}

int main() {
    cin>>n>>m;
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            cin>>a[i][j];
        }
    }

    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            if(!g[i][j]) {
                v.clear();
                dfs(i,j,a[i][j]);
                st.insert(norm());
            }
        }
    }
    cout<<st.size();

    return 0;
}


/*
in:
5 6
1 2 3 4 4 5
1 2 3 3 4 5
1 2 2 3 4 5
1 6 6 7 7 8
6 6 7 7 8 8

out:
7
*/

【参考文献】
https://www.luogu.com.cn/problem/solution/P10379
https://gesp.ccf.org.cn/101/attach/1602047231131680.pdf

相关推荐
xhchen20233 年前
第 380 场 LeetCode 周赛题解
数学·算法·leetcode·二分·字符串哈希
theSerein3 年前
【马蹄集】第二十二周——进位制与字符串专题
字典树·马蹄集试题题解·mt2175 五彩斑斓的串·mt2173 回文串等级·mt2182 新十六进制数·字符串哈希·进位制