题目翻译
为了识别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;
}