?題幹
????????為了理解早期文明,考古學家經常學習用古代語言寫的文本。有一個這樣的語言,最早被使用於三千年前的埃及。這種語言是基於象形文字的符號。下圖是六種象形文字和它們的名稱。在這個問題中,你將寫一個程序來識別這六種符號。
?輸入
??????? 這些輸入包含多個測試樣例,每個樣例表示的圖像包含一個或多個上面的符號。這些圖像以水平掃描線包含的黑色坐標和白色坐標的形式給出。在這些輸入數據中,每個掃描線被編碼成十六進制。例如,序列八個坐標"10011100"(一個黑像素,接著兩個白像素……)被表示成16進制的"9c"。16進制中只使用數字和小寫字母。每個樣例的第一行輸入包含兩個整數(H 和 W)H(0< H <= 200)表示行,W(0 < W <= 50)表示列。輸入的圖像數據從上到下。
輸入圖像形式滿足一下規則:
- 圖像僅包含上面的六種符號;
- 每個圖像至少有一個有效的符號;
- 每個黑色坐標是符號的一部分;
- 每個象形文字有一組連接的黑色像素組成,每個黑色像素在其頂部、底部、左側、右側、至少有一個其他黑色像素;
- 符號之間不互相重合並且不存在包含;
- 兩個對角線接觸的黑色像素總是有一個共同的接觸黑色像素
- 符號可以被扭曲,但每個符號都有一個拓撲上等同於上圖中的符號(兩個圖形如果在拓撲上是等價的,就意味著可以在不撕裂的情況下拉伸成另一個圖形)
??????? 最後的樣例輸入包含兩個0
輸出
??????? 對於每一個樣例,使用以下代碼顯示它的樣例編號,後面跟著一個字符串,該字符串包含在圖像中識別的每一個象形文字的一個字符。每一個輸出的字符串,按照字母順序打印
Ankh: A????????Wedjat: J????????Djed: D????????Scarab: S????????Was: W????????Akhet: K
樣例輸入
100 25
0000000000000000000000000
0000000000000000000000000
...(50 lines omitted)...
00001fe0000000000007c0000
00003fe0000000000007c0000
...(44 lines omitted)...
0000000000000000000000000
0000000000000000000000000
150 38
00000000000000000000000000000000000000
00000000000000000000000000000000000000
...(75 lines omitted)...
0000000003fffffffffffffffff00000000000
0000000003fffffffffffffffff00000000000
...(69 lines omitted)...
00000000000000000000000000000000000000
00000000000000000000000000000000000000
?樣例輸出
Case 1: AKW
Case 2: AAAAA
思路
????????通過數洞可以發現,六種符號的白色洞數分別為1 3 5 0 2。因此只需要通過DFS判斷它們的內部的白色聯通塊數量就可以做到識別文字。 大概編程思路如下:
- 先將數據從十六進制轉換成二進制
- DFS整塊矩陣。分別將聯通塊標號。底部的白色標記為首先標記為1。這裡的聯通塊包括:底部,黑色邊界,黑色邊界內的白色塊。
- 然後分別根據和對於黑色邊界相關的白色塊數量,判定符號。
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <string>
using namespace std;
string hextobin(const string &hex); //進制轉換,一個16進制數轉換成四個二進制數
void dfs(int row, int col, int count);
void calhole(int row, int col); //統計黑色邊框相應的白色孔數
unordered_map<char, string> hexmp; //將二進制與十六機制對應起來
unordered_map<int, unordered_set<int>> nhole; //相應黑邊對應的孔的標記值
vector<string> pic; //底板
vector<vector<int>> tag; //標記板
int h, m; //行列
int direction[4][2] = {{1, 0}, {-1, 0}, {0, -1}, {0, 1}}; //表示dfs的四個方向
int main()
{
hexmp['0'] = "0000";
hexmp['1'] = "0001";
hexmp['2'] = "0010";
hexmp['3'] = "0011";
hexmp['4'] = "0100";
hexmp['5'] = "0101";
hexmp['6'] = "0110";
hexmp['7'] = "0111";
hexmp['8'] = "1000";
hexmp['9'] = "1001";
hexmp['a'] = "1010";
hexmp['b'] = "1011";
hexmp['c'] = "1100";
hexmp['d'] = "1101";
hexmp['e'] = "1110";
hexmp['f'] = "1111";
const char *wordmp = "WAKJSD"; //將孔數與文字對應起來
int n = 0;
while (cin >> h >> m && h)
{
getchar(); //去除換行符
m = m * 4 + 2; //轉換成二進制之後,列數變為原來四倍。且添加了倆列
pic.clear();
pic.push_back(string(m, '0')); //在首行添加全是0的一行
string hex;
for (int i = 0; i < h; i++)
{
getline(cin, hex); //讀取一行
pic.push_back("0" + hextobin(hex) + "0"); //將這一行轉換成二進制,然後存儲到底板pic中,在首列添加全是0的一列
}
pic.push_back(string(m, '0')); //在尾行添加全是0的一行
h += 2; //添加了二行
tag.clear();
for (int i = 0; i < h; i++)
tag.push_back(vector<int>(m, 0)); //生成h行m列的0矩陣。用以記錄“用數字標記聯通塊”的數據
int count = 0; //聯通塊標記值,同時也代表聯通塊數量
nhole.clear();
for (int i = 0; i < h; i++) //dfs所有坐標
for (int j = 0; j < m; j++)
if (tag[i][j] == 0)
{
dfs(i, j, ++count);
if (pic[i][j] == '1') //記錄是黑邊的標號
nhole[count]; //如果nhole中沒有下標為count,則添加,如果有,不進行操作,如果這裡不做統計,就會漏掉W,因為W內部沒有白色聯通塊,calhole無法統計
}
for (int i = 0; i < h; i++)
for (int j = 0; j < m; j++)
if (pic[i][j] == '1') //如果是黑色邊框
calhole(i, j);
string ans;
ans.clear();
for (auto hole : nhole) //將每一個的孔數轉換成文字
ans.push_back(wordmp[hole.second.size()]);
sort(ans.begin(), ans.end());
cout << "Case " << ++n << ": " << ans << endl;
}
return 0;
}
string hextobin(const string &hex)
{
string bin{""};
for (auto i : hex)
bin += hexmp[i];
return bin;
}
void dfs(int row, int col, int count)
{
tag[row][col] = count; //標記當前坐標值
for (auto i : direction)
{
int row2 = row + i[0];
int col2 = col + i[1];
//新坐標要在範圍內、沒被標記過而且與舊坐標的顏色相同
if (row2 >= 0 && row2 < h && col2 >= 0 && col2 < m && tag[row2][col2] == 0 && pic[row2][col2] == pic[row][col])
dfs(row2, col2, count);
}
}
void calhole(int row, int col)
{
for (auto i : direction)
{
int row2 = row + i[0];
int col2 = col + i[1];
/*如果標記值在合理範圍內、不相等而且新坐標的標記值不是1(即不是白色背板)
就把標記值添加到相應的黑色標記值之下。
注意:將有重複的標記值添加到某一個黑色標記值之下,這裡利用set元素不重複性質*/
if (row2 >= 0 && row2 < h && col2 >= 0 && col2 < m && tag[row][col] != tag[row2][col2] && tag[row2][col2] != 1)
nhole[tag[row][col]].insert(tag[row2][col2]);
}
}
|