UVA

来源:互联网 发布:数据分析 用户画像 编辑:程序博客网 时间:2024/05/21 00:54
/*  收获:  1. 概念理解 图形的拓扑等价  可见: http://www.baike.com/wiki/%E6%8B%93%E6%89%91    摘取重点:  在拓扑学里不讨论两个图形全等的概念,但是讨论拓扑等价的概念。比如,尽管圆和方形、三角形的形状、大小不同,在拓扑变换下,它们都是等价图形。在一个球面上任选一些点用不相交的线把它们连接起来,这样球面就被这些线分成许多块。在拓扑变换下,点、线、块的数目仍和原来的数目一样,这就是拓扑等价。一般地说,对于任意形状的闭曲面,只要不把曲面撕裂或割破,他的变换就是拓扑变换,就存在拓扑等价。    2. 这个博客的讲解比较清晰:  http://www.cnblogs.com/acm1314/p/4534360.html    3. blog: http://blog.csdn.net/ecnu_lzj/article/details/71056490    这个博客,通过举例子,解释清楚了一个问题:  为什么上下左右,都要加上边界(这是为了,通过连通块的计算,来判断图里到底有几个洞)    不过最终,由于这道题还是不怎么会写,所以我自己没能写出来,而是看懂了小白书上的代码后,按照书的配套代码的思路敲的  */


#include <cstdio>#include <cstring>#include <algorithm>#include <vector>#include <set>#define rep(i, n) for (int i = 0; i < (n); i++)using namespace std;char bin[256][5];const int maxh = 200 + 5;const int maxw = 50 * 4 + 5;int H, W, pic[maxh][maxw], color[maxh][maxw]; //col: colorchar line[maxw];const int dx[] = { -1, 1, 0, 0 };const int dy[] = { 0, 0, -1, 1 };//遍历连通块时用,对应的是四个方向的坐标变化量 vector<set<int> > neighbours;const char code[] = "WAKJSD";//通过黑连通块的序号,找到它内部的白连通块个数(即:象形文字内的洞的个数),最终返回洞数所对应的字母 char change(int c){int cnt = neighbours[c].size();return code[cnt];}void print() //这个函数可以用来做测试,看十六进制转换为二进制以后,转换结果是否正确 {rep(i, H){rep(j, W) printf("%d", pic[i][j]);printf("\n");}}void decode (char ch, int row, int col){rep(i, 4)pic[row][col + i] = bin[ch][i] - '0'; //把每个十六进制字符,转为二进制时,一个字符变为对应变为四个字符}bool isin(int x, int y){return x >= 0 && x < H && y >= 0 && y < W;}bool isok(int x, int y) //判断 (x, y)是否在迷宫区域内,且尚未被染色 {return isin(x, y) && !color[x][y];}//从位置(row, col)开始进行dfs,并将与之相邻的整个连通块,设置为颜色c void dfs(int row, int col, int c){color[row][col] = c;rep(i, 4){int row2 = row + dx[i];int col2 = col + dy[i];if (isok(row2, col2) && pic[row2][col2] == pic[row][col]) //判断对应位置二进制数码是否一致dfs(row2, col2, c); }}void check_neighbours(int row, int col){rep(i, 4){int row2 = row + dx[i], col2 = col + dy[i];if (isin(row2, col2) && !pic[row2][col2] && color[row2][col2] != 1)neighbours[color[row][col]].insert(color[row2][col2]);//cnt为1的一圈(因为经过了color数组的赋值,所以也即为,color数组中,对应元素为1的圈) 其实是象形文字的最外圈,也就是我们加的四周边界,以及,四周边界和象形文字之间,还会有一些区域和四周边界连接,这整个的连通块,其实和象形文字本身无关,故而不做处理}}int main(){strcpy(bin['0'], "0000"); //用来将十六进制转换为二进制,很巧妙的方法,就是先将十六进制对应的二进制字符存好,到时就可以直接使用了 strcpy(bin['1'], "0001");strcpy(bin['2'], "0010");strcpy(bin['3'], "0011");strcpy(bin['4'], "0100");strcpy(bin['5'], "0101");strcpy(bin['6'], "0110"); strcpy(bin['7'], "0111");strcpy(bin['8'], "1000");strcpy(bin['9'], "1001");strcpy(bin['a'], "1010");strcpy(bin['b'], "1011");strcpy(bin['c'], "1100");strcpy(bin['d'], "1101");strcpy(bin['e'], "1110");strcpy(bin['f'], "1111");int kase = 0;while (scanf("%d%d", &H, &W) == 2 && H){memset(pic, 0, sizeof(pic));rep(i, H){scanf("%s", line);rep(j, W)decode (line[j], i + 1, j * 4 + 1); //将十六进制的输入,转换为其对应的二进制,因而每个char,将会转换为4个字符 }H += 2;W = W * 4 + 2; //上下左右方向,都要在最外层加边界。否则在统计连通块时,有可能会在特殊的图形分布处出错int cnt = 0;vector<int> cc; //对应二进制数位为1的连通块 (connnected components)memset(color, 0, sizeof(color));rep(i, H)rep(j, W)if (!color[i][j]){dfs(i, j, ++cnt);if (pic[i][j] == 1) cc.push_back(cnt);//扫描整个区域,遍历找到所有连通块,并为同一连通块的格子,标上相同的编号。同时,把黑连通块存进 vector cc中 } neighbours.clear();neighbours.resize(cnt + 1);//neighbours vector中,存放的是int型集合,所以最好先自己 resize设置大小//之所以要 +1,是因为有 dfs(i, j, ++cnt); 使得cnt其实是从1开始的,而不是0;而cnt的意义,恰好是当前的黑色连通块个数,而这样的设定,和vector中的下标的意义是有区别的(毕竟前者从1开始,后者从0开始),这点在设置vector的大小时,要尤其注意 rep(i, H)rep(j, W){if (pic[i][j] == 1)check_neighbours(i, j);//扫描整个图形中的黑点,并把该点旁边的白色连通块(白色连通块个数,就代表了洞数)存入 vector neighbours//vector中的下标,恰好是黑点对应的连通块的编号}vector<char> ans;rep(i, cc.size())ans.push_back(change(cc[i]));sort(ans.begin(), ans.end());printf("Case %d: ", ++kase);rep(i, ans.size()) printf("%c", ans[i]);printf("\n");}return 0;}

原创粉丝点击