(图的遍历专题整理)

来源:互联网 发布:xy苹果助手 网络 编辑:程序博客网 时间:2024/05/17 06:08

         这一篇博客继续以一些OJ上的题目为载体,对搜索专题进行整理整理一下。会陆续的更新。。。


一、DFS在图的遍历中的使用


1、HDU 1241 Oil Deposits

题目分析:

这道题是一道简单的DFS的题目(当然也可以用其他方法来做).题目主要意思是求有多少块油田。算法就是:

如果map[i][j]是一块油田就不断的对他进行DFS。


/* * HDU_1241.cpp * *  Created on: 2014年6月5日 *      Author: Administrator */#include <iostream>#include <cstdio>using namespace std;const int maxn = 105;char map[maxn][maxn];//n:行数..m:列数int n,m;int dir[8][2] = {//定义方向矩阵{-1,1},{-1,0},{-1,-1},{0,1},{0,-1},{1,1},{1,0},{1,-1}};/** * 判断边界 */bool checkBound(int i,int j){//(i,j):第i行,第j列if(i < 0 || i >= n || j < 0 || j >= m){return false;}return true;}/** * 深搜 */void dfs(int i,int j){//访问(i,j)map[i][j] = '*';//将(i,j)设置为已访问int ii;for(ii = 0 ; ii < 8 ; ++ii){//遍历这个点的所有相邻的点int x = dir[ii][0];int y = dir[ii][1];int xx = i + x;int yy = j + y;if(checkBound(xx,yy) && map[xx][yy] == '@'){//如果这个相邻点还没有被访问dfs(xx,yy);}}}int main(){while(scanf("%d%d",&n,&m),n||m){int count = 0;int i;int j;for(i = 0 ; i < n ; ++i){//*****特别要注意这种字符矩阵的数据的读入的方式...scanf("%s",&map[i]);}for(i = 0 ; i < n ; ++i){for(j = 0 ; j < m ; ++j){if(map[i][j] == '@'){dfs(i,j);count++;}}}printf("%d\n",count);}return 0;}


2、POJ 2386 Lake Counting


题目分析:

这道题和上面的那道是一样的。。

/* * POJ_2386.cpp * *  Created on: 2014年6月5日 *      Author: Administrator */#include <iostream>#include <cstdio>using namespace std;const int maxn = 105;char map[maxn][maxn];int n, m;//方向千万别写错了,否则会WA的int dir[8][2] = { { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, 1 }, { 0, -1 },{ 1, 1 }, { 1, 0 }, { 1, -1 } };bool checkBound(int i, int j) {if (i < 0 || i >= n || j < 0 || j >= m) {return false;}return true;}void dfs(int i, int j) {map[i][j] = '.';int ii;for (ii = 0; ii < 8; ++ii) {int xx = i + dir[ii][0];int yy = j + dir[ii][1];if(checkBound(xx,yy) && map[xx][yy] == 'W'){dfs(xx,yy);}}}int main(){while(scanf("%d%d",&n,&m)!=EOF){int count = 0;int i;for(i = 0 ; i < n ; ++i){scanf("%s",map[i]);}int j;for(i = 0 ; i < n ; ++i){for(j = 0 ; j < m ; ++j){if(map[i][j] == 'W'){dfs(i,j);count++;}}}printf("%d\n",count);}return 0;}


二、拓扑排序在图的遍历中的使用


1、WIKIOI  2833 奇怪的梦境


题目分析:

1)题目可以抽象为:“判断一幅图是否能生成一个拓扑序列,如果不能输出不满足拓扑序列的元素的个数”

使用链式前向星建图,然后进行拓扑排序,然后输出拓扑序列。(第一道使用链式前向星来做的题,好兴奋)

2)每行两个数ai,bi,表示bi按钮要在ai之后按下.其实这个就可以建立一条从a指向b的边.......


/* * WIKIOI_2833.cpp * *  Created on: 2014年6月6日 *      Author: Administrator */#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn = 10005;const int maxm = 25009;//链式前向星int head[maxn];//head[i]: 以节点i为顶点的第一条边的位置(边号)struct Edge{int to;//终点int weight;//权值.(这道题中没有用到)int next;//edge[i].next: 与第i条边同起点的下一条边的位置(边号)}edge[maxm];int indegree[maxn];//indegree[i]: 顶点i的入度int n,m;/** * 拓扑排序:不断的删除入度为0的顶点及其出边。直到没有顶点 */void topo(){int queue[maxn];//用来记录最后的拓扑序列int iq = 0;//用来记录拓扑序列中元素的个数..int i;for(i = 1 ; i <= n ; ++i){//寻找入度为0的顶点if(indegree[i] == 0){queue[iq++] = i;}}for(i = 0 ; i < iq ; ++i){int k;for(k = head[queue[i]] ; k != -1 ; k = edge[k].next){indegree[edge[k].to]--;if(indegree[edge[k].to] == 0){queue[iq++] = edge[k].to;}}}if(iq < n){//如果iq的最终值小于n.说明拓扑序列不存在...printf("T_T\n");printf("%d\n",n-iq);}else{printf("o(∩_∩)o\n");}}int main(){while(scanf("%d%d",&n,&m)!=EOF){int i;int cnt = 0;memset(head,-1,sizeof(head));memset(indegree,0,sizeof(indegree));for(i = 1 ; i <= m ; ++i){int a,b;scanf("%d%d",&a,&b);indegree[b]++;//该顶点的入度+1edge[cnt].to = b;edge[cnt].next = head[a];head[a] = cnt++;}topo();}}



2、UVA 10305 Ordering tasks

题目分析:

输出一个合法的拓扑序列即可(不必和样例保持一致)...


/* * UVA_10305.cpp * *  Created on: 2014年6月6日 *      Author: Administrator */#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn = 105;const int maxm = 10005;int head[maxn];struct Edge{int to;int weight;int next;}edge[maxm];int indegree[maxn];int n,m;void topo(){int queue[maxn];int iq = 0;int i;for(i = 1 ; i <= n ; ++i){if(indegree[i] == 0){queue[iq++] = i;}}for(i = 0 ; i < iq ; ++i){int k;for(k = head[queue[i]] ; k != -1; k = edge[k].next){indegree[edge[k].to]--;if(indegree[edge[k].to] == 0){queue[iq++] = edge[k].to;}}}for(i = 0 ; i < iq-1 ; ++i){printf("%d ",queue[i]);}printf("%d\n",queue[iq-1]);}int main(){while(scanf("%d%d",&n,&m),n||m){int i;int cnt = 0;memset(head,-1,sizeof(head));memset(indegree,0,sizeof(indegree));for(i = 1 ; i <= m ; ++i){int a,b;scanf("%d%d",&a,&b);indegree[b]++;edge[cnt].to = b;edge[cnt].next = head[a];head[a] = cnt++;}topo();}return 0;}


三、图的可行遍性

      图的可行遍性通俗点讲,就是图在某种特定的条件下能否被遍历。

      1、欧拉图(求欧拉回路)

       问题描述:从起点出发,每一条边经过一次以后返回到起点。输出相应的序列(可能是边序列,也可能是点序列)

       欧拉图问题的算法实现的基本代码:

       

/** * 求欧拉回路的基本代码 */int ans[maxm];//欧拉回路int ansi;int visited[maxm];//用来标记某一条边是否被访问过int edgeNum;void addEdge(int u,int v){edge[edgeNum].to = v;edge[edgeNum].next = head[u];head[u] = edgeNum++;}/** * 用来求欧拉回路 */void dfs(int now){int k;for(k = head[now]; k != -1 ; k = edge[k].next){//遍历以某个点为起点的所有边if(visited[k] == false){//如果这条边没有被访问过visited[k] = true;//就将这条边标记为已经访问dfs(edge[k].to);//继续搜索这条边的终点ans[ansi++] = edge[k].to;//这样求的是欧拉回路中的点序号.要注意对终点元素手动加上..//ans[ansi++] = k;//如果这样的话,求的是欧拉回路中的边序号}}//ans[ansi++] = now;//printf("%d\n",now);}

以上欧拉图算法的代码是基于链式前向星的。以下是链式前向星的基本代码:

/** * 链式前向星的基本结构 */int head[maxn];struct Edge{int to;int weight;int next;};Edge edge[maxm];

       算法的时间复杂度:O(m)

  

例题:

1)POJ 2230 Watch Cow

题目与分析:

     这一道题抽象一下,可以描述为:“从起点出发,经过每条边2次,再返回起点,输出所经过的点序列”。其实就是欧拉图的简单变形。本题与基本的欧拉图的问题的区别就在于将便变成双向边即可。。


/* * POJ_2230.cpp * *  Created on: 2014年6月7日 *      Author: Administrator */#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn = 10005;const int maxm = 100005;/** * 链式前向星的基本结构 */int head[maxn];struct Edge{int to;int weight;int next;};Edge edge[maxm];/** * 求欧拉回路的基本代码 */int ans[maxm];//欧拉回路int ansi;int visited[maxm];//用来标记某一条边是否被访问过int edgeNum;//链式前向星添加一条边的操作void addEdge(int u,int v){edge[edgeNum].to = v;edge[edgeNum].next = head[u];head[u] = edgeNum++;}/** * 用来求欧拉回路 */void dfs(int now){int k;for(k = head[now]; k != -1 ; k = edge[k].next){//遍历以某个点为起点的所有边if(visited[k] == false){//如果这条边没有被访问过visited[k] = true;//就将这条边标记为已经访问dfs(edge[k].to);//继续搜索这条边的终点ans[ansi++] = edge[k].to;//这样求的是欧拉回路中的点序号.要注意对终点元素手动加上..//ans[ansi++] = k;//如果这样的话,求的是欧拉回路中的边序号}}//ans[ansi++] = now;//printf("%d\n",now);}int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){int i;memset(head,-1,sizeof(head));edgeNum = 0;memset(visited,false,sizeof(visited));ansi = 0;for(i = 1 ; i <= m ; ++i){int a,b;scanf("%d%d",&a,&b);addEdge(a,b);//用来解决每条边通过两次的问题addEdge(b,a);}dfs(1);ans[ansi++] = 1;//这里手动加上起点for(i = 0 ; i < ansi; ++i){printf("%d\n",ans[i]);}}return 0;}



     




     














9 0
原创粉丝点击