• UVA


    /*
      收获:
      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: color
    char 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("
    ");
    	}
    }
    
    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("
    ");
    	}
    	return 0;
    }

  • 相关阅读:
    WinForm界面开发之布局控件"WeifenLuo.WinFormsUI.Docking"的使用
    分享几个.NET WinForm开源组件,纪念逐渐远去的WinForm。。。
    小议主子表INT自增主键插入记录的方法SQL server]教程
    微软工程师主讲的SqlServer2005视频教程
    11 个重要的数据库设计规则
    sql数据库设计学习---数据库设计规范化的五个要求
    YY淘宝商品数据库设计
    ASP.NET MVC的客户端验证:jQuery的验证
    ASP.NET MVC的客户端验证:jQuery验证在Model验证中的实现
    UNIX标准化及实现之POSIX标准可选头文件
  • 原文地址:https://www.cnblogs.com/mofushaohua/p/7789396.html
Copyright © 2020-2023  润新知