UVA1103 古代象形符号 Ancient Messages解题报告(DFS,字符串)

百般思念 提交于 2020-02-19 03:24:12

题目翻译

为了识别3000年前古埃及用到的6种象形文字。每组数据包含一个H行W列的字符矩阵(H≤200,W≤50 ),每个字符为4个相邻像素点的十六进制(例如,10011100对应的字符就是9c)。转化为二进制后1表示黑点,0表示白点。输入满足以下条件:

不会出现上述6种符号之外的其他符号。
输入至少包含一个符号,且每个黑像素都属于一个符号。输入至少包含一个符号,且每个黑像素都属于一个符号。
每个符号都是一个四连块,并且不同符号不会相互接触,也不会相互包含。
如果两个黑像素有公共顶点,则它们一定有一个相同的相邻黑像素(有公共边)。
符号的形状一定和题中的图形拓扑等价(可以随意拉伸但不能拉断)。

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

在这里插入图片描述

输出说明:
For each test case, display its case number followed by a string containing one character for each
hieroglyph recognized in the image, using the following code:
Ankh: A
Wedjat: J
Djed: D
Scarab: S
Was: W
Akhet: K
在这里插入图片描述
Sample Input

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
0 0

Sample Output

Case 1: AKW
Case 2: AAAAA

那么根据《算法竞赛入门经典(第二版)》中关于本题的分析

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

可以得知我们需要知道有几个白洞就好了
根据题意,我们需要把输入的十六进制转化为二进制来显示地图,1是黑边,0是白边。
那么我们用一个unordered_map<char,string>f16t2用来把十六进制转化为二进制地图,注意要把地图的一圈加上一层白边,然后进行深搜,先把黑边外面的所有的白点+2,然后再搜里面,有几个白洞用ans表示一下,并用unordered_map<int,char>toans找到答案并加到result里输出即可
C++11 unordered_map详细介绍
以下代码必须在C++11的编译环境中才能编译通过(最新版Code::Blocks如何设置C++11

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e3+7;
const ll mod=2147483647;
ll h,w,ans,dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
unordered_map<char,string>f16t2={//把十六进制转换成二进制
  {'0',"0000"}, {'1',"0001"}, {'2',"0010"}, {'3',"0011"},
  {'4',"0100"}, {'5',"0101"}, {'6',"0110"}, {'7',"0111"},
  {'8',"1000"}, {'9',"1001"}, {'a',"1010"}, {'b',"1011"},
  {'c',"1100"}, {'d',"1101"}, {'e',"1110"}, {'f',"1111"}
};
unordered_map<int,char>toans={{1,'A'},{3,'J'},{5,'D'},{4,'S'},{0,'W'},{2,'K'}};//根据白洞的数量判断答案
vector<string>mp;//存地图
void dfs(ll x,ll y,char ch,ll &ans)
{
    mp[x][y]=(char)(ch+2);//把1变成3,把2变成4
    for(int k=0;k<4;++k)
    {
        ll nx=x+dir[k][0],ny=y+dir[k][1];//查连通块
        if(nx>=0&&nx<mp.size()&&ny>=0&&ny<mp[x].size()){
            if(ch=='1'&&mp[nx][ny]=='0'){//黑边里面有白洞就ans++
                ans++;
                dfs(nx,ny,'0',ans);
            }
            if(ch==mp[nx][ny])//两边相同就继续查下去
                dfs(nx,ny,ch,ans);
        }
    }
}
int main()
{
    int h,w;
    for(int ii=1;~scanf("%d%d%*c",&h,&w)&&h!=0;++ii){//%*c会自动忽略最后一个符号“*”符:用以表示该输入项,读入后不赋予相应的变量,即跳过该输入值。
        mp.resize(h+2);//分配内存
        mp.front()=string(w*4+2,'0');//填一层白边
        for(int i=1;i<=h;++i)
        {
            mp[i]="0";//再填一层白边
            string s;
            getline(cin,s);
            for(char ch:s)
                mp[i]+=f16t2[ch];//利用string的特性更方便
            mp[i]+="0";//再填一层白边
        }
        mp.back()=string(w*4+2,'0');//再填一层白边
        ll ans=0;
        string result="";//答案
        dfs(0,0,'0',ans);//先把外层的0搜完并全部改成2
        for(int i=0;i<mp.size();++i)
            for(int j=0;j<mp[i].size();++j){
                if(mp[i][j]=='1')
                {
                    ans=0;
                    dfs(i,j,'1',ans);
                    result+=toans[ans];//收集答案
                }
            }
        sort(result.begin(),result.end());//按字典序排序
        printf("Case %d: %s\n",ii,result.c_str());//把string转换成C语言风格的字符串再用printf输出
    }
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!