Flood Fill + 雜湊 - 星空之夜 - AcWing 1402
Flood Fill + 雜湊 - 星空之夜 - AcWing 1402
一個星群是指一組非空的在水平,垂直或對角線方向相鄰的星星的集合。
一個星群不能是一個更大星群的一部分。
星群可能是相似的。
如果兩個星群的形狀、包含星星的數目相同,那麼無論它們的朝向如何,都認為它們是相似的。
通常星群可能有 8 種朝向,如下圖所示:
現在,我們用一個二維 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,j且i=j∑distance(vi,vj)=i,j且i=j∑(xvi−xvj)2+(yvi−yvj)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;
}