问题描述
问题输入输出
输入
输出
输入输出样例
Input:
6 2
00
7c
44
7c
30
00
6 25
0000000000000000000000000
0000000000000000000000000
00001fe0000000000007c0000
00003fe0000000000007c0000
0000000000000000000000000
0000000000000000000000000
10 3
000
778
548
748
578
700
000
7f0
1e0
000
16 2
00
7e
42
7e
42
7e
42
7e
42
7e
42
7e
00
00
4a
00
16 1
0
1
3
2
8
e
f
5
7
6
4
c
d
9
b
a
9 2
00
7e
42
7e
42
7e
42
7e
00
Output:
Case 1: A
Case 2: WW
Case 3: AKW
Case 4: DWWW
Case 5: AWW
Case 6: J
题目大意
- 输入一个高、宽为h、w的字符矩阵,一个字符表示一个16进制数,来表示输入的图像
- 每个图像至少包含一个象形文字
- 象形文字之间不接触、不包含
- 象形文字可能会被拉伸,但是拓扑结构上是等效的
- 象形文字是“四连通的”,即一个象形文字的黑色元素至少有一个上、下、左或者右是其他黑色元素。
题目分析
- 对于每种象形文字,我们应该需要找到其独有的“特征向量”,通过观察,我们发现象形文字内部的空洞(白洞)数目是不同的,分别对应于
A=1; J=3; D=5; S=4; W=0; K=2
- 所以我们可以使用DFS深度优先遍历来数象形文字内部的空洞数目,以此来判断是哪种象形文字。
但是我们会遇到以下问题:
- 怎么将输入的16进制数,转成2进制数,进而转换成图像种的0/1矩阵?
16进制数用4位二进制数来表示,我们可以每次向右移动一位取出4为二进制数字。或者提前写好16个数对应的二进制数表示。 - 怎么数象形文字内部的空洞数目?
- 先用DFS扣除外部背景图像(由四周向中间的背景都要抠出来)
- 然后对每个象形文字进行一次DFS,若在DFS时遇到内部空洞,则对空洞进行一次DFS并计数
- 最后返回统计的空洞数目
/* Ancient Messages */
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<queue>
#include<functional>
using namespace std;
//#define DEBUG
const int maxh = 210;
const int maxw = 60;
const int width = 4; // 一个字符对应的像素数量
int h, w;
char line[maxw]; // 对应于输入的一行字符
int image[maxh][maxw * width];
char word[6] = { 'W','A','K','J','S','D' }; // 各个空洞对应的象形文字
int mov[4][2] = { {0,1},{0,-1},{1,0},{-1,0} }; // 移动的4个方向
void printMap() { // 打印填充后的图形
for (int i = 0; i < h; i++) {
for (int j = 0; j < 4 * w; j++) printf("%d", image[i][j]);
printf("\n");
}
}
int isRight(int x, int y) {
// 该位置是否在图内
if (x < 0 || x >= h || y < 0 || y >= width * w) return 0;
return 1;
}
int char2int(char c) {
if (c >= '0' && c <= '9') return c - '0';
else return c - 'a' + 10;
}
void dfs_background(int x, int y, int val) {
// 填充背景,填充值为val,默认为-1
image[x][y] = val;
for (int i = 0; i < 4; i++) {
int nx = x + mov[i][0];
int ny = y + mov[i][1]; // 下一次移动的位置
if (isRight(nx, ny) && image[nx][ny] == 0) dfs_background(nx, ny, val);
// 没遍历过的背景
}
}
void dfs_word(int x, int y, int& cnt) {
//printf("遍历汉字,位置(%d,%d)\n", x, y);
image[x][y] = -1; // 暂时标记成背景,也可以标记成其他元素,比如文字对应的id
for (int i = 0; i < 4; i++) {
int nx = x + mov[i][0];
int ny = y + mov[i][1]; // 下一次移动的位置
if (isRight(nx, ny) && image[nx][ny] == 1) dfs_word(nx, ny, cnt);
else if (isRight(nx, ny) && image[nx][ny] == 0) {
// 内部空洞
cnt++;
dfs_background(nx, ny, -1);
}
}
}
int main() {
int kcase = 1;
while (cin >> h >> w && h) {
priority_queue<char, vector<char>, greater<char> > ans; // 答案
memset(image, 0, sizeof(image));
for (int i = 0; i < h; i++) {
scanf("%s", line);
for (int j = 1; j <= w; j++) { // 将line转换成二进制存储
int num = char2int(line[j-1]);
for (int k = 4 * j - 1; k >= 4 * j - 4; k--) { // 第j个字符对应的二进制
image[i][k] = (num & 1); // 取出最低位
num >>= 1; // 右移一位
}
}
}// 输入完毕
#ifdef DEBUG
printf("输入的图像\n");
printMap();
#endif
/*
** 这里需要注意一个问题,扣背景的时候需要从4周开始从外向内扣
** 因为有些文字可能与边缘接触,导致从(0,0)起点开始扣覆盖面不全
*/
for (int i = 0; i < h; i++) {
if (image[i][0] == 0) dfs_background(i, 0,-1);
if (image[i][4 * w - 1] == 0) dfs_background(i, 4 * w - 1,-1);
}
for (int i = 0; i < 4 * w; i++) {
if (image[0][i] == 0) dfs_background(0, i, -1);
if (image[h - 1][i] == 0) dfs_background(h - 1, i, -1);
}
#ifdef DEBUG
printf("背景填充后\n");
printMap();
#endif
for (int i = 0; i < h; i++) {
for (int j = 0; j < width * w; j++) {
if (image[i][j] == 1) {// 表示一个文字
int cnt = 0; // 空洞数目
dfs_word(i, j, cnt);
ans.push(word[cnt]);
}
}
}// 遍历完成
printf("Case %d: ", kcase); kcase++;
while (ans.size()) {
char c = ans.top(); ans.pop();
printf("%c", c);
}
printf("\n");
}
return 0;
}