例题6-13 UVA1103 Ancient Messages(59行AC代码)

紫书刷题进行中,题解系列【GitHub|CSDN

例题6-13 UVA1103 Ancient Messages(59行AC代码)

题目大意

给定6种象形字符及其对于的标记字母,现用16进制字符表示一张图像,以字典序输出图像中的所有象形字符。有以下简化约定:

  • 字符仅包括给定的6种
  • 每个图像至少包含一个象形字符
  • 每个黑色像素均属于一个象形字符
  • 字符间相互独立,即不相交,不包含
  • 处于对角位置的像素也算相邻
  • 图像可以拉伸

思路分析

一道十分有趣的题目,值得琢磨。通过以下三个核心问题来讨论解决思路

问题1:如何分辨不同象形字符?
回答1:图像内部的白洞个数

这里需要一点想象力和观察力,既然图像可以拉伸变换,因此必须要找到一个不变的特征量,仔细观察所给出的6个图像,可以发现每个图像内部所包含的白洞个数依次为:1,3,5,4,0,2。正好6个数无重复值,因此可以用图像的内部的白洞个数来辨别字符

问题2:怎么判断白色块属于哪个黑色连通块内部?
回答2:单个拷贝

若一张图中仅有一个黑色连通块,那么它的白色连通块可轻易计算出来,但存在多个黑色连通块时,就无法直接判定每个黑色连通块内部的白色块个数了。因此,假设原图存于img1,遍历其中一个黑色连通块的同时,拷贝到img2,此时img2中只包含一个黑色连通块,即可轻易计数

注意:直接拷贝的方式存在一个bug
解决:给拷贝的img2四周增加一个白色像素框(上下左右均多加一个)

当图中仅含一个黑色连通块时,令黑色像素点与边界围成白色块,会导致白色块增加。因此,可在img2四周增加一圈白框,连通外围白色块,避免以上bug

算法设计

先将16进制字符串转换为4位的2进制字符串,可用bitset快速实现

定义string img[205], img2[205]; 分别表示原图像,加一圈白边的图像

枚举img中的黑色连通块,对于每个黑色连通块,dfs过程中同步拷贝到img2,拷贝完毕后,dfs计算img2中的白色块个数

  • 对于8个方向可用方向向量或一个二重循环处理
  • 映射转换关系可用map或哈希表实现,简化代码

AC代码(C++11,思维题,dfs)

#include<bits/stdc++.h>
using namespace std;
int H, W, num=0;
string img[205], img2[205], s; // 原图像,加一轮白边的图像
map<char, string> mp; // 16进制字符->4位的2进制字符串
char word[6] = {'W', 'A', 'K', 'J', 'S', 'D'}; // 象形字内部对应的白色块个数
void trans() { // 一位16进制字符[0,f]转为4位的二进制字符串
    char c[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
    for (int i = 0; i < 16; i ++) {
        bitset<4> bt(i); // 10->二进制数值->二进制字符串
        mp[c[i]] = bt.to_string();
    }
}
void dfs(int x, int y, char tag) { // tag='0':遍历img的一个黑色连通块,并拷贝到img2;tag='0':计算img2的白色连通块个数
    if (tag == '0') img[x][y] = '0';
    if (tag == '0') img2[x+1][y+1] = '1'; // 白色边框
    else img2[x][y] = '1';
    int xx, yy, h, w;
    for (int i = -1; i <= 1; i ++) { // 8个方向
        for (int j = -1; j <= 1; j ++) {
            if (i == 0 && j == 0) continue; // 不写也行,自身已经被赋值为1了
            xx = x + i; yy = y + j;
            h = (tag == '1') ? H+2 : H; w = (tag == '1') ? 4*W+2 : 4*W; // 边界控制
            if (xx >= 0 && xx < h && yy >= 0 && yy < w) {
                if (tag == '0' && img[xx][yy] == '1' || tag == '1' && img2[xx][yy] == '0') dfs(xx, yy, tag);
            }
        }
    }
}
int main() {
    trans(); // 16转二进制字符串初始化 
    while (cin >>H >>W && (H != 0 && W != 0)) {
        for (int i = 0; i < H; i ++) {
            cin >>s; img[i].clear();
            for (int j = 0; j < s.size(); j ++) img[i].append(mp[s[j]]); // 转为2进制
        }
        vector<char> res; // 保存结果
        for (int i = 0; i < H; i ++) {
            for (int j = 0; j < 4*W; j ++) {
                if (img[i][j] == '1') { // 发现一个黑色块
                    for (int k=0; k < H+2; k ++) img2[k] = string(4*W+2, '0'); // 初始化
                    dfs(i, j, '0'); // 拷贝连通块
                    int cnt=0; // 统计白色块个数
                    for (int i2 = 0; i2 < H+2; i2 ++) { // img2计算白色洞个数
                        for (int j2 = 0; j2 < 4*W+2; j2 ++) { // 注意长度和宽度
                            if (img2[i2][j2] == '0') {cnt++; dfs(i2, j2, '1');}
                        }
                    }
                    res.push_back(word[cnt-1]); // 存储对应象形字符结果
                }
            } 
        }
        sort(res.begin(), res.end()); // 字典序排列
        printf("Case %d: ", ++num);
        for (auto c : res) printf("%c", c);
        puts("");
    }
    return 0;
}
发布了128 篇原创文章 · 获赞 87 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_40738840/article/details/104333616
今日推荐