Hash函数 + FloodFills

1402. 星空之夜 - AcWing题库

思路:

大体思路就是找所有的连通块(FooldFills算法(DFS)) 

然后对于每个连通块,判断是否是重复出现的形状,这是个难点

我们不可能把一个形状旋转个90°或者180°之类的

那么我们最好的方法就是把一个形状对应成一个尽量唯一的数

就是哈希表了,我们把一个形状映射成一个数的好处就是这个形状无论是那个方向,我们都不需要考虑,我们只需要考虑这个块本身,而不需要考虑这个块在图中的位置之类。

对于哈希表,我们知道最重要的是哈希函数,哈希函数需要解决的一个核心问题就是处理冲突,这里的哈希函数直接背过就行了:hash(set)= sum(C(n,2)),意思就是在这个连通块set中任选两个点(有C(n,2))中选择,计算他们距离的总和,注意这个总和是要开平方的,不然引起冲突的概率会很大。

那么对于每一个连通块,我们求他的哈希值,然后保存下来,方便以后的连通块查询,然后转变成小写字母就行了,需要注意的是,在比较的时候,由于距离开根号了,是个浮点数,所以我们直接等于,浮点数是可能存在误差的,所以我们要让他们两个相减的绝对值小于一个很小的数(1e-6/1e-8)就行了。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 110;
const double eps = 1e-8;

int n, m, top;
char g[N][N];
PII q[N * N];//开N^2
//哈希表
double Hash[N];
int id;

double get_dist(PII a, PII b)
{
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    double res = sqrt(dx * dx + dy * dy);
    return res;
}

double get_Hash()//sum:C(c,2)
{
    double sum = 0;
    for(int i = 0; i < top; i ++ )
        for(int j = i + 1; j < top; j ++ )
            sum += get_dist(q[i], q[j]);
    return sum;    
}

char get_id(double key)
{
    // static double Hash[30]; //静态,全局变量,一共26个英文字母,所以大小开30即可
    // static int id = 0;
    for(int i = 0; i < id; i ++ )
        if(fabs(Hash[i] - key) < eps)   //如果重复出现,浮点数比较不能直接等于
            return i + 'a';
            
    Hash[id ++ ] = key; //没出现过,保存到哈希表中
    return id - 1 + 'a';
}

void dfs(int a, int b)//八个方向
{
    q[top ++ ] = {a, b};//放入集合中
    g[a][b] = '0';//遍历过了直接在原图标记一下
    for(int x = a - 1; x <= a + 1; x ++ )//遍历所有相邻的点
        for(int y = b - 1; y <= b + 1; y ++ )
        {
            if(x == a && y == b)    continue;//中心点跳过
            if(x < 0 || x >= n || y < 0 || y >= m || g[x][y] != '1')    
                continue;//越界或者不合法跳过
            dfs(x, y);
        }
}

int main()
{
    cin >> m >> n;
    for(int i = 0; i < n; i ++ )    cin >> g[i];
    // for(int i = 0; i < n; i ++ )    cout << g[i] << endl;
    
    for(int i = 0; i < n; i ++ )
    {
        for(int j = 0; j < m; j ++ )
        {
            if(g[i][j] == '1')//遍历到一个连通块
            {
                top = 0;//清空之前连通块里面的所有点
                dfs(i, j);//搜索当前连通块
                char c = get_id(get_Hash());//通过哈希值得到id
                // cout << "C: " << c << endl;
                // cout << "sum: " << top << endl;
                for(int k = 0; k < top; k ++ )
                    g[q[k].x][q[k].y] = c;//修改原图中的点
            }
        }
    }
    
    for(int i = 0; i < n; i ++ )    cout << g[i] << endl;
    
    return 0;
}

posted @ 2022-05-05 08:41  光風霽月  阅读(14)  评论(0编辑  收藏  举报