题目描述:题目很长,就贴一下题目连接吧=_=。。/problem/UVA-1103
大意是输入一个H行W列的字符矩阵(H<=200, W<=50)。每个字符为4个相邻像素点的十六进制(即10011100对应的是9c),这样可以得到一副图片,然后找出图片中包含的题目中给出的六种象形符号是哪些。
思路:这是紫书上的一个例题,作者给出了一个重要的提示,即找这几个象形符号的特征在每个符号内部的空白数不同,我们只要找出每个黑色联通块内部有几个空白,就能知道这个黑色联通块对应哪个符号,找联通块用dfs即可。按照这个思路,我的做法是将每个黑色联通块都从原始图像中提取出来,其背景为白色,这样数有多少个白色联通块,就能确定是哪个符号。最后花了很长时间才把程序调试出来,有几个细节没有注意,比如需要对原始图片上下左右各添加一列空白,这样可以避免在边界时出现多将空白判定为黑色联通块内部的情况。
代码如下:
1 #include <iostream> 2 #include <memory.h> 3 #include <string> 4 #include <algorithm> 5 using namespace std; 6 const int maxn = 206; 7 int pix[maxn][maxn]; 8 int idx[maxn][maxn]; 9 int mcopy[maxn][maxn][maxn]; //mcopy[i][][]表示将第i个象形文字提取出来后得到的图片 10 int H, W; 11 const char code[6] = {'W', 'A', 'K', 'J', 'S', 'D'}; 12 13 void dfs(int r, int c, int id, int symbol) //这是对原始图片dfs的函数,用来找黑色联通块,以及提取每个黑色联通块到mcopy中 14 { 15if(r < 0 || r > H+1 || c < 0 || c > W*4+1) return; 16if(idx[r][c] > 0 || pix[r][c] != symbol) return; 17 18 19 // cout << "r: " << r << " c: " << c << "\n"; 20idx[r][c] = id; 21mcopy[id-1][r][c] = 1; 22dfs(r-1, c, id, symbol); 23dfs(r+1, c, id, symbol); 24dfs(r, c-1, id, symbol); 25dfs(r, c+1, id, symbol); 26 } 27 28 void dfs(int r, int c, int id, int symbol, int i) //这是对mcopy处理的函数,找其中白色联通块的个数 29 { 30//cout << "r: " << r << "c: " << c << "\n"; 31static int n = 1; 32//cout << n++ << "\n"; 33if(r < 0 || r > H+1 || c < 0 || c > W*4+1) return; 34if(idx[r][c] > 0 || mcopy[i][r][c] != 0) return; 35idx[r][c] = id; 36dfs(r-1, c, id, symbol, i); 37dfs(r+1, c, id, symbol, i); 38dfs(r, c-1, id, symbol, i); 39dfs(r, c+1, id, symbol, i);40 } 41 42 int chartonum(char c) 43 { 44if(isalpha(c)) return c-'a'+10; 45else return c-'0'; 46 } 47 48 int main() 49 { 50//freopen("uva1103_in.txt", "r", stdin); 51//freopen("uva1103_out.txt", "w", stdout); 52int kase = 0; 53char c; 54 55while(cin >> H >> W && H){56 memset(pix, 0, sizeof(pix)); //之前这里清零的位置放在了while外面,导致wa。每处理一个新的图片都要将此前的图片清零 57 int val, y = 1; 58 for(int i = 1; i <= H; ++i){ //输入处理操作 59 y = 1; 60 for(int j = 0; j < W; ++j){ 61 while((c = getchar()) == '\n'); 62 val = chartonum(c); 63 for(int x = 0; x < 4; ++x){ 64pix[i][y] = (val & 0x08) >> 3; 65y = (y+1) % (4*W+1); 66val = val << 1; 67 } 68 } 69 } 70 memset(idx, 0, sizeof(idx)); 71 memset(mcopy, 0, sizeof(mcopy)); 72 int cnt = 0; 73 for(int i = 0; i < H+2; ++i){ //对原始图像进行处理,cnt是原始图像中象形文字的个数 74 for(int j = 0; j < W*4+2; ++j){ 75 if(idx[i][j] == 0 && pix[i][j] == 1){ 76 dfs(i, j, ++cnt, 1); 77 } 78 } 79 } 80 printf("Case %d: ", ++kase); 81 string ans; 82 for(int i = 0; i < cnt; ++i){ //对每个象形文字 83 memset(idx, 0, sizeof(idx)); //访问状态数组idx清零 84 int num = 0;85 for(int j = 0; j < H+2; ++j){ 86 for(int k = 0; k < W*4+2; ++k){ 87 if(idx[j][k] == 0 && mcopy[i][j][k] == 0){ //数有几个白块 88dfs(j, k, ++num, 0, i); //如果(j,k)位置没有被访问 && (j,k)上是一个空白像素点, 从它开始dfs, 找到一个联通块 89 } 90 } 91 } 92 ans += code[num-1]; //num是白色联通块的个数,这样就能对应出是哪一个象形文字,加入ans中 93 } 94 sort(ans.begin(),ans.end()); //按字母大小序排列 95 cout << ans << "\n"; 96 ans.clear(); 97} 9899 100 }
PS: 看了作者的实现代码,也将其记录下来,学习~
其主要思路是用dfs给各个联通块涂上不同颜色,将是象形文字的联通块单独保存,再对原图像的每个像素,检查其四周的邻居,neighbors[color]<set>就保存各个颜色联通块与周围哪些颜色块相邻。最后看单独保存的象形文字联通块的neighbors的大小,就可以得到其内部有多少“空洞”。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<vector> 5 #include<set> 6 using namespace std; 7 8 char bin[256][5]; 9 10 const int maxh = 200 + 5; 11 const int maxw = 50 * 4 + 5; 12 13 int H, W, pic[maxh][maxw], color[maxh][maxw]; 14 char line[maxw]; 15 16 void decode(char ch, int row, int col) { 17 for(int i = 0; i < 4; i++) 18pic[row][col+i] = bin[ch][i] - '0'; 19 } 20 21 const int dr[] = {-1, 1, 0, 0}; 22 const int dc[] = {0, 0, -1, 1}; 23 24 // dfs from (row, col) and paint color c 25 void dfs(int row, int col, int c) { 26 color[row][col] = c; 27 for(int i = 0; i < 4; i++) { 28int row2 = row + dr[i]; 29int col2 = col + dc[i]; 30if(row2 >= 0 && row2 < H && col2 >= 0 && col2 < W && pic[row2][col2] == pic[row][col] && color[row2][col2] == 0) //下一步在范围内&&是相同性质联通块(白色块or象形文字)&&未被访问 31 dfs(row2, col2, c); 32 } 33 } 34 35 vector<set<int> > neighbors; 36 37 void check_neighbors(int row, int col) { 38 for(int i = 0; i < 4; i++) { 39int row2 = row + dr[i]; 40int col2 = col + dc[i]; 41if(row2 >= 0 && row2 < H && col2 >= 0&& col2 < W && pic[row2][col2] == 0 && color[row2][col2] != 1)//相邻像素块 在范围内&&性质是白色联通块&&该白色联通块不是整个背景 42 neighbors[color[row][col]].insert(color[row2][col2]); 43 } 44 } 45 46 const char* code = "WAKJSD"; 47 48 char recognize(int c) { 49 int cnt = neighbors[c].size(); 50 return code[cnt]; 51 } 52 53 // use this function to print the decoded picture 54 void print() { 55 for(int i = 0; i < H; i++) { 56for(int j = 0; j < W; j++) printf("%d", pic[i][j]); 57printf("\n"); 58 } 59 } 60 61 int main() { 62 strcpy(bin['0'], "0000"); 63 strcpy(bin['1'], "0001"); 64 strcpy(bin['2'], "0010"); 65 strcpy(bin['3'], "0011"); 66 strcpy(bin['4'], "0100"); 67 strcpy(bin['5'], "0101"); 68 strcpy(bin['6'], "0110"); 69 strcpy(bin['7'], "0111"); 70 strcpy(bin['8'], "1000"); 71 strcpy(bin['9'], "1001"); 72 strcpy(bin['a'], "1010"); 73 strcpy(bin['b'], "1011"); 74 strcpy(bin['c'], "1100"); 75 strcpy(bin['d'], "1101"); 76 strcpy(bin['e'], "1110"); 77 strcpy(bin['f'], "1111"); 78 79 int kase = 0; 80 while(scanf("%d%d", &H, &W) == 2 && H) { 81memset(pic, 0, sizeof(pic)); 82for(int i = 0; i < H; i++) { 83 scanf("%s", line); 84 for(int j = 0; j < W; j++) 85 decode(line[j], i+1, j*4+1); 86} 87 88H += 2; 89W = W * 4 + 2; 90 91int cnt = 0; 92vector<int> cc; // connected components of 1 93memset(color, 0, sizeof(color)); 94for(int i = 0; i < H; i++) 95 for(int j = 0; j < W; j++) 96 if(!color[i][j]) { 97 dfs(i, j, ++cnt); 98 if(pic[i][j] == 1) cc.push_back(cnt); 99 }100 101neighbors.clear();102neighbors.resize(cnt+1);103for(int i = 0; i < H; i++)104 for(int j = 0; j < W; j++)105 if(pic[i][j] == 1)106 check_neighbors(i, j);107 108vector<char> ans;109for(int i = 0; i < cc.size(); i++)110 ans.push_back(recognize(cc[i]));111sort(ans.begin(), ans.end());112 113printf("Case %d: ", ++kase);114for(int i = 0; i < ans.size(); i++) printf("%c", ans[i]);115printf("\n");116 }117 return 0;118 }