1. 程式人生 > 其它 >Flood Fill + 雜湊 - 星空之夜 - AcWing 1402

Flood Fill + 雜湊 - 星空之夜 - AcWing 1402

技術標籤:雜湊DFS字串演算法ACMDFS雜湊

Flood Fill + 雜湊 - 星空之夜 - AcWing 1402

一個星群是指一組非空的在水平,垂直或對角線方向相鄰的星星的集合。

一個星群不能是一個更大星群的一部分。

星群可能是相似的。

如果兩個星群的形狀、包含星星的數目相同,那麼無論它們的朝向如何,都認為它們是相似的。

通常星群可能有 8 種朝向,如下圖所示:

starry-1.gif

現在,我們用一個二維 01 矩陣來表示夜空,如果一個位置上的數字是 1,那麼說明這個位置上有一個星星,否則這個位置上的數字應該是 0。

給定一個夜空二維矩陣,請你將其中的所有星群用小寫字母進行標記,標記時相似星群用同一字母,不相似星群用不同字母。

標註星群就是指將星群中所有的 1 替換為小寫字母。

輸入格式

第一行包含一個整數 W,表示矩陣寬度。

第二行包含一個整數 H,表示矩陣高度。

接下來 H 行,每行包含一個長度為 W 的 01 序列,用來描述整個夜空矩陣。

輸出格式

輸出標記完所有星群后的二維矩陣。

用小寫字母標記星群的方法很多,我們將整個輸出讀取為一個字串,能夠使得這個字串字典序最小的標記方式,就是我們想要的標記方式。

輸出這個標記方式標出的最終二維矩陣。

資料範圍

0≤ W,H ≤100,
0≤ 星群數量 ≤500,
0≤ 不相似星群數量 ≤26,
1≤ 星群中星星的數量 ≤160

輸入樣例:

23
15
10001000000000010000000
01111100011111000101101
01000000010001000111111
00000000010101000101111
00000111010001000000000
00001001011111000000000
10000001000000000000000
00101000000111110010000
00001000000100010011111
00000001110101010100010
00000100110100010000000
00010001110111110000000
00100001110000000100000
00001000100001000100101
00000001110001000111000

輸出樣例:

a000a0000000000b0000000
0aaaaa000ccccc000d0dd0d
0a0000000c000c000dddddd
000000000c0b0c000d0dddd
00000eee0c000c000000000
0000e00e0ccccc000000000
b000000e000000000000000
00b0f000000ccccc00a0000
0000f000000c000c00aaaaa
0000000ddd0c0b0c0a000a0
00000b00dd0c000c0000000
000g000ddd0ccccc0000000
00g0000ddd0000000e00000
0000b000d0000f000e00e0b
0000000ddd000f000eee000

分析:

① : 從 前 到 後 , 從 上 到 下 , 將 圖 中 每 一 個 連 通 塊 摳 出 來 。 ①:從前到後,從上到下,將圖中每一個連通塊摳出來。

② : 將 這 個 連 通 塊 保 存 下 來 , 以 備 後 續 查 詢 。 如 果 該 連 通 塊 出 現 過 , 則 將 該 連 通 塊 替 換 成 對 應 的 字 符 ; 否 則 按 順 序 給 一 個 新 的 字 符 。 ②:將這個連通塊儲存下來,以備後續查詢。\\\qquad如果該連通塊出現過,則將該連通塊替換成對應的字元;否則按順序給一個新的字元。

對 於 問 題 ① , 採 用 F l o o d F i l l 算 法 , 直 接 搜 索 。 對於問題①,採用Flood\ Fill 演算法,直接搜尋。 FloodFill

對 於 問 題 ② , 可 以 給 每 一 個 連 通 塊 一 個 哈 希 值 , 接 下 來 考 慮 哈 希 函 數 : 對於問題②,可以給每一個連通塊一個雜湊值,接下來考慮雜湊函式:

哈 希 函 數 應 滿 足 : Ⅰ 、 與 連 通 塊 的 形 狀 有 關 , 與 方 向 和 位 置 無 關 。 Ⅱ 、 避 免 衝 突 , 若 衝 突 , 則 增 加 哈 希 函 數 。 \qquad 雜湊函式應滿足:\\\qquadⅠ、與連通塊的形狀有關,與方向和位置無關。\\\qquadⅡ、 避免衝突,若衝突,則增加雜湊函式。 滿

哈 希 函 數 : 考 慮 順 序 地 計 算 連 通 塊 內 各 點 之 間 的 距 離 ( 歐 幾 裡 得 距 離 ) 之 和 。 即 : \qquad 雜湊函式:考慮順序地計算連通塊內各點之間的距離(歐幾里得距離)之和。即: ()

h a s h = ∑ i , j 且 i ≠ j d i s t a n c e ( v i , v j ) = ∑ i , j 且 i ≠ j ( x v i − x v j ) 2 + ( y v i − y v j ) 2 hash=\sum_{i,j且i≠j}distance(v_i,v_j)=\sum_{i,j且i≠j}\sqrt{(x_{v_i}-x_{v_j})^2+(y_{v_i}-y_{v_j})^2} hash=i,ji=jdistance(vi,vj)=i,ji=j(xvixvj)2+(yviyvj)2

具體落實:

① 、 從 前 到 後 遍 歷 連 通 塊 , 每 次 把 連 通 塊 內 所 有 點 的 坐 標 保 存 下 來 。 ①、從前到後遍歷連通塊,每次把連通塊內所有點的座標儲存下來。

② 、 計 算 當 前 連 通 塊 的 哈 希 值 , 並 查 表 比 對 。 若 存 在 該 哈 希 值 , 則 將 該 連 通 塊 改 為 指 定 符 號 ; 若 不 存 在 , 按 順 序 將 該 連 通 塊 改 為 下 一 個 字 母 , 同 時 在 表 中 記 錄 該 字 母 的 哈 希 值 。 ②、計算當前連通塊的雜湊值,並查表比對。\\\ \\ \qquad若存在該雜湊值,則將該連通塊改為指定符號;\\ \ \\\qquad若不存在,按順序將該連通塊改為下一個字母,同時在表中記錄該字母的雜湊值。

程式碼:

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

#define P pair<int,int>
#define x first
#define y second

using namespace std;

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

int n, m;
char s[N][N];
bool st[N][N];
char flag = 'a';
double H[30];
int d;
P q[N*2];
int top;

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

double get_hash()
{
    double sum = 0;
    for(int i=0;i<top;i++)
        for(int j=i+1;j<top;j++)
            sum += get_dis(q[i], q[j]);
    return sum;
}

void turn(int k)
{
    for(int i=0;i<top;i++)
    {
        int x = q[i].x, y = q[i].y;
        s[x][y] = flag + k;
    }
}

void dfs(int x, int y)
{
    q[top ++] = {x, y};
    st[x][y] = true;
    for(int i=x-1;i<=x+1;i++)
        for(int j=y-1;j<=y+1;j++)
            if(i>0 && i<=n && j>0 && j<=m && s[i][j] == '1' && !st[i][j])
                dfs(i, j);
}

void solve()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(!st[i][j] && s[i][j] == '1')
            {
                top = 0;
                dfs(i, j);
                double hash = get_hash();
                bool exist = false;
                for(int i=0;i<d;i++)
                    if(fabs(hash - H[i]) < eps)
                    {
                        turn(i);
                        exist = true;
                        break;
                    }
                if(!exist)
                {
                    turn(d);
                    H[d] = hash;
                    ++ d;
                }
            }
}

int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    solve();
    for(int i=1;i<=n;i++) printf("%s\n",s[i]+1);
    
    return 0;
}