UVA ~ 1103 ~Ancient Messages (思维 + 图的联通块的应用)

题意:本题的目的是识别3000年前古埃及用到的六种象形文字,如图6-10所示。

每组数据包含一个H行W列的字符矩阵(H≤200,W≤50),每个字符为4个相邻像素点的十六进制(例如,10011100对应的字符就是9c)。转化为二进制后1表示黑点,0表示白点。输入满足:

□不会出现上述6中符号之外的符号。

□输入至少包含一个符号,且每个黑像素都属于一个符号。

□每个符号都是一个四连块,并且不同符号不会相互接触,也不会相互包含。

□如果两个黑像素有公共定点,则他们一定有一个相同的相邻黑像素(有公共边)(这句话好像没什么用,忽略掉就好,两个不同的图形是不会有交点或包含的

□符号形状一定和表6-9中的图形拓扑等价(可以随意拉伸但不能拉断)

要求按照字典序输出所有符号。

【分析】

“随意拉伸但不能拉断”是一个让人头疼的条件。怎么办呢?看来不能拘泥于细节,而要从全局考虑,找到一个易于计算,而且在“随意拉伸”时还不会改变的“特征量”,通过计算和比较“特征量”完成识别。题目说过,每个符号都是一个四连块,即所有的黑点都连在一起,二中间有一些白色的“洞”。数一数就能发现,题目表中的6个符号从左到右依次有1,3,5,4,0,2个洞,各部相同。这样,只需要数一数输入的符号有几个“白洞”,就能准确的知道他是哪个符号了。

以上内容来自《算法竞赛入门经典》

补充:我们已经知道了数洞的方法以后,首先把十六进制的数字转化为二进制后,我们可以在外层围一圈0,这样就可以把背景中的白块连到一起,然后从(0,0)位置开始进行求联通块并编号(黑色跟黑色联通,白色跟白色联通)。

然后遍历整个地图,如果该点为黑色点且联通块编号为[i],我们就把[i]号联通块附近的白色的点放入neighbors[i]中,因为我们只需要知道在这个第[i]号黑色联通块中有多少个白洞(即白色联通块),我们用这样一个结构vector <set<int> > neighbors就可以了。如果[i]号联通块为黑色,那么neighbors[i]里面元素个数就是“白洞”个数,那么就可以知道是哪个图形了,存到一个string中,排序输出即可。


#include<bits/stdc++.h>
using namespace std;
const int MAXH = 200 + 5;
const int MAXW = 50 * 4 + 5;
const char* code = "WAKJSD";//洞数对应的图形字母
const int dir[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};//四个方向
map<char, string> bin;
int n, m, color[MAXH][MAXW];
string line[MAXH], MAP[MAXH];
void decode()//重构地图,并在外围包一圈白块
{
    for (int i = 0; i <= n+1; i++) MAP[i].clear();
    for (int i = 1; i <= n; i++)
    {
        MAP[i] += "0";//每行第一个字符
        for (int j = 0; j < m; j++)
            MAP[i] += bin[line[i][j]];
        MAP[i] += "0";//每行最后一个字符
    }
    n += 2; m = m*4 + 2;
    //最后一行跟第一行加0
    for (int i = 0; i < m; i++) MAP[0] += "0", MAP[n-1] += "0";
    //for (int i = 0; i < n; i++) cout << MAP[i] << endl; cout << endl; //输出地图
}
void DFS(int x, int y, int c)//联通块编号
{
    color[x][y] = c;
    for (int i = 0; i < 4; i++)
    {
        int dx = x + dir[i][0], dy = y + dir[i][1];
        if (dx >= 0 && dy >= 0 && dx < n && dy < m && MAP[dx][dy] == MAP[x][y] && !color[dx][dy])
            DFS(dx, dy, c);
    }
}
vector <set<int> > neighbors;//neighbors[i]表示i这个黑点边上有哪些白色点
void check_neighbors(int x, int y)//统计(x,y)这个黑色像素四周白色点颜色
{
    for (int i = 0; i < 4; i++)
    {
        int dx = x + dir[i][0], dy = y + dir[i][1];
        //不越界,且是白点,且不是背景(外面分割各个图形)的白点
        if (dx >= 0 && dy >= 0 && dx < n && dy < m && MAP[dx][dy] == '0' && color[dx][dy] != 1)
            neighbors[color[x][y]].insert(color[dx][dy]);
    }
}
int main()
{
    //freopen("C:\\Users\\张松超\\Desktop\\in.txt", "r", stdin);
    //freopen("C:\\Users\\张松超\\Desktop\\out.txt", "w", stdout);
    bin['0'] = "0000"; bin['1'] = "0001"; bin['2'] = "0010"; bin['3'] = "0011";
    bin['4'] = "0100"; bin['5'] = "0101"; bin['6'] = "0110"; bin['7'] = "0111";
    bin['8'] = "1000"; bin['9'] = "1001"; bin['a'] = "1010"; bin['b'] = "1011";
    bin['c'] = "1100"; bin['d'] = "1101"; bin['e'] = "1110"; bin['f'] = "1111";
    int CASE = 1;
    while (~scanf("%d%d", &n, &m) && n)
    {
        for (int i = 1; i <= n; i++) cin >> line[i];
        decode();
        //联通块编号
        memset(color, 0, sizeof(color));
        int cnt = 1;
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < m; j++)
            {
                if (!color[i][j]) DFS(i, j, cnt++);
            }
        }

        neighbors.clear();
        neighbors.resize(cnt+1);
        set<int> black;//黑色联通块的编号
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < m; j++)
            {
                if (MAP[i][j] == '1')
                {
                    check_neighbors(i, j);
                    black.insert(color[i][j]);
                }
            }
        }
        string ans;
        for (auto i: black)
        {
            int t = neighbors[i].size();//t个洞
            ans += code[t];
        }
        sort(ans.begin(), ans.end());
        printf("Case %d: %s\n", CASE++, ans.c_str());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值