算是很复杂的题了(起码对我来说)。彻底弄懂之后梳理一下解法步骤吧:
- 把十六进制的输入转成二进制保存。
- 在保存的图像外圈新增一个白圈。这样做是为了把符号处在图像边缘时切割开的白色部分全部连在一起,以便于之后着色。实际操作时我是在存放的时候就留出了左侧和上方的空间,然后在深搜的时候把四个方向的空行全部加上,间接加了白圈。
- 外层的白色全部着色后,剩余的白色就只剩下符号内部的白洞了。这时对每个符号分别进行深搜,把黑色块换成新的颜色,遇到白色块时把与这个白色块连通的白色块全部着成同种颜色,同时计数器加一,表示有一个白洞。这样就实现了对符号内部白洞的查找。
- 最后按照白洞的个数识别对应的符号。
输出时记得按照字典序排列!
由于最后转换成的二进制序列最大可达到200*200,而深搜会用到递归,所以在遇到较多数据时会导致栈溢出的情况。但是UVa并没有报错,还是AC了。我愿称其为侥幸AC。
题目链接:UVa 1103
AC代码:
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 200 + 10;
const char* character = "WAKJSD";
char reference[16][5] = { "0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111" };
int m, n, cnt, bit[maxn][maxn], color[maxn][maxn];
char input[maxn][maxn];
vector<char> v;
void _dfs(int r, int c, int id) { //给bit为0的块上色,第一次上色后剩余的0块都是符号内部的0了
if (bit[r][c] != 0 || color[r][c] != 0) return;
if (r < 0 || r > m + 1 || c < 0 || c > n * 4 + 1) return; //r从0到m+1的闭区间,c为从0到n*4+1的闭区间,实现了在原图外侧加“白圈”
color[r][c] = id;
for (int dr = -1; dr <= 1; dr++)
for (int dc = -1; dc <= 1; dc++)
if (dr != 0 || dc != 0)
_dfs(r + dr, c + dc, id);
}
void dfs(int r, int c, int id) { //遍历bit为1的块,再次遇到0时cnt++,同时把所有的连通块上色
if (r < 0 || r > m + 1 || c < 0 || c > n * 4 + 1) return;
if (bit[r][c] == 0 && color[r][c] == 0) {
_dfs(r, c, id + 1);
cnt++;
return;
}
if (bit[r][c] == 0 || color[r][c] != 0) return;
color[r][c] = id;
for (int dr = -1; dr <= 1; dr++)
for (int dc = -1; dc <= 1; dc++)
if (dr != 0 || dc != 0)
dfs(r + dr, c + dc, id);
}
void H_to_B(string s, int r) { //十六进制转二进制
int len = s.length();
for (int i = 0, j = 1; i < len; i++) { //j=1保证左侧有多一列空列,便于将无关块一起上色
if (s[i] >= 48 && s[i] <= 57)
for (int k = j; j < k + 4; j++)
bit[r][j] = reference[s[i] - 48][j - k] - '0';
else
for (int k = j; j < k + 4; j++)
bit[r][j] = reference[s[i] - 97 + 10][j - k] - '0';
}
}
int main() {
int kase = 1;
while (cin >> m && m) {
cin >> n;
int num = n * 4;
memset(color, false, sizeof(color));
memset(bit, 0, sizeof(bit));
for (int i = 0; i < m; i++) {
cin >> input[i];
H_to_B(input[i], i + 1); //保证上方多一行空行,便于将无关块一起上色
}
_dfs(0, 0, -1);
for (int i = 0; i < m + 1; i++)
for (int j = 0; j < n * 4 + 1; j++)
if (color[i][j] == 0 && bit[i][j] == 1) {
cnt = 0;
dfs(i, j, 1);
v.push_back(character[cnt]); //存储结果,便于排序
}
sort(v.begin(), v.end()); //字典序排列
cout << "Case " << kase++ << ": ";
for (auto& p : v)
cout << p;
v.clear();
cout << endl;
}
return 0;
}