Uva1103 古代象形符号

题目描述:题目很长,就贴一下题目连接吧=_=。。https://vjudge.net/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 {
 15     if(r < 0 || r > H+1 || c < 0 || c > W*4+1) return;
 16     if(idx[r][c] > 0 || pix[r][c] != symbol) return;
 17 
 18 
 19 //    cout << "r: " << r << " c: " << c << "\n";
 20      idx[r][c] = id;
 21     mcopy[id-1][r][c] = 1;
 22     dfs(r-1, c, id, symbol);
 23     dfs(r+1, c, id, symbol);
 24     dfs(r, c-1, id, symbol);
 25     dfs(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";
 31     static int n = 1;
 32     //cout << n++ << "\n";
 33      if(r < 0 || r > H+1 || c < 0 || c > W*4+1) return;
 34     if(idx[r][c] > 0 || mcopy[i][r][c] != 0) return;
 35     idx[r][c] = id;
 36     dfs(r-1, c, id, symbol, i);
 37     dfs(r+1, c, id, symbol, i);
 38     dfs(r, c-1, id, symbol, i);
 39     dfs(r, c+1, id, symbol, i);    
 40 }
 41 
 42 int chartonum(char c)
 43 {
 44     if(isalpha(c)) return c-'a'+10;
 45     else return c-'0';
 46 }
 47 
 48 int main()
 49 {
 50     //freopen("uva1103_in.txt", "r", stdin);
 51     //freopen("uva1103_out.txt", "w", stdout);
 52     int kase = 0;
 53     char c;
 54 
 55     while(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){
 64                         pix[i][y] = (val & 0x08) >> 3;
 65                         y = (y+1) % (4*W+1);
 66                         val = 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){  //数有几个白块 
 88                         dfs(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     }
 98     
 99 
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++)
 18     pic[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++) {
 28     int row2 = row + dr[i];
 29     int col2 = col + dc[i];
 30     if(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++) {
 39     int row2 = row + dr[i];
 40     int col2 = col + dc[i];
 41     if(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++) {
 56     for(int j = 0; j < W; j++) printf("%d", pic[i][j]);
 57     printf("\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) {
 81     memset(pic, 0, sizeof(pic));
 82     for(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 
 88     H += 2;
 89     W = W * 4 + 2;
 90 
 91     int cnt = 0;
 92     vector<int> cc; // connected components of 1
 93     memset(color, 0, sizeof(color));
 94     for(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 
101     neighbors.clear();
102     neighbors.resize(cnt+1);
103     for(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 
108     vector<char> ans;
109     for(int i = 0; i < cc.size(); i++)
110       ans.push_back(recognize(cc[i]));
111     sort(ans.begin(), ans.end());
112 
113     printf("Case %d: ", ++kase);
114     for(int i = 0; i < ans.size(); i++) printf("%c", ans[i]);
115     printf("\n");
116   }
117   return 0;
118 }

 

posted @ 2019-07-30 22:12  patrolli  阅读(596)  评论(0编辑  收藏  举报