方法:邻接矩阵(数组打表)和邻接表(指针连接)
多组输入,到文件结尾。
每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v代表u到v有一条有向边。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入两个数为a,b。
0 1
2
0 1
1 0
No
#include <iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=5000; bool connection[maxn][maxn];//int会超时,bool一个字节,所以比较快?? int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { memset(connection,0,sizeof(connection));//用cin和cout会超时!而用scanf和printf不会!说明了cin和cout很慢了! for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); connection[a][b]=1;//有边标记即可,若为无向图则需要双向标记! } int q; scanf("%d",&q); while(q--) { int a,b; scanf("%d%d",&a,&b); if(connection[a][b]==1) printf("Yes\n"); else printf("No\n"); } } return 0; }
struct Node { int date; Node *next; }; Node *paint[maxn]; if(paint[a]==NULL)//此节点没有就更新! { paint[a]=new Node; paint[a]->date=b; paint[a]->next=NULL; } else { p=new Node; p->date=b; p->next=paint[a]->next;//把所有边存下来连接起来! paint[a]->next=p;//关键! }
每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v代表u到v有一条有向边。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入两个数为a,b。
注意:点的编号为0~n-1,2<=n<=500000 ,0<=m<=500000,0<=q<=500000,a!=b,输入保证没有自环和重边
0 1
2
0 1
1 0
No
#include <iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=500000;//数据过大,使用邻接矩阵超内存,只能使用邻接表!! struct Node { int date; Node *next; }; Node *paint[maxn]; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { Node *p,*t; memset(paint,0,sizeof(paint)); for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); if(paint[a]==NULL)//此节点没有就更新! { paint[a]=new Node; paint[a]->date=b; paint[a]->next=NULL; } else { p=new Node; p->date=b; p->next=paint[a]->next;//把所有边存下来连接起来! paint[a]->next=p; } } int q; scanf("%d",&q); while(q--) { int a,b; scanf("%d%d",&a,&b); t=paint[a]; bool judge=false; while(t!=NULL) { if(t->date==b) { judge=true; break; } t=t->next;//查找时顺着链子依次查找! } if(judge) printf("Yes\n"); else printf("No\n"); } } return 0; }
每一组第一行有两个数n、m表示n个点,m条有向边。接下来有m行,每行两个数u、v、w代表u到v有一条有向边权值为w。第m+2行有一个数q代表询问次数,接下来q行每行有一个询问,输入一个数为a
注意:点的编号为0~n-1,2<=n<=500000 ,0<=m<=500000,0<=q<=500000,u!=v,w为int型数据。输入保证没有自环和重边
权值小的在前。
权值相等的边出发点编号小的在前
- 权值和出发点相等的到达点编号小的在前
注:边的编号自0开始
0 1 1
1 2 2
1 3 0
3
0
1
2
0 1
1 2
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=500000; struct Node { int start,ending,val; bool operator<(const Node &A)const{//重载一下 if(val!=A.val)return val<A.val; else if(start!=A.start)return start<A.start; else return ending<A.ending; } }node[maxn]; int main() { int n,m; while(cin>>n>>m) { for(int i=0;i<m;i++) { int a,b,v; scanf("%d%d%d",&a,&b,&v); node[i].start=a; node[i].ending=b; node[i].val=v; } sort(node,node+m);//! int q; scanf("%d",&q); while(q--) { int instruct; scanf("%d",&instruct); printf("%d %d\n",node[instruct].start,node[instruct].ending); } } return 0; }
它从图中某个结点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中的所有顶点都被访问到为止。
基本实现思想:
(1)访问顶点v;
(2)从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
(1)访问顶点v;visited[v]=1;//算法执行前visited[n]=0
(2)w=顶点v的第一个邻接点;
(3)while(w存在)
if(w未被访问)
从顶点w出发递归执行该算法;
w=顶点v的下一个邻接点;
非递归实现
(1)栈S初始化;visited[n]=0;
(2)访问顶点v;visited[v]=1;顶点v入栈S
(3)while(栈S非空)
x=栈S的顶元素(不出栈);
if(存在并找到未被访问的x的邻接点w)
访问w;visited[w]=1;
w进栈;
else
void dfs(int p) { vis[p]=1; for(int j=1; j<=n; j++) { if(sign[p][j]&&!vis[j]) { dfs(j); } } }
{
while(! s.empty())
for(j= 0;j<G.n;j++) // 访问与顶点i相邻的顶点
{
visited[j]= true ;
break; // 找到一个相邻未访问的顶点,访问之后则跳出循环
}
s.pop();
对于每组测试数据:
第一行两个整数n, m,表示迷宫有n * m 个格子。(1 <= n, m <= 6, (n, m) !=(1, 1) ) 接下来n 行,每行m 个数。其中第i 行第j 个数是0 表示第i 行第j 个格子可以走,否则是1 表示这个格子不能走,输入保证起点和终点都是都是可以走的。
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int maps[10][10];//地图 int vis[10][10];//标记是否走过 int dh[]={-1,1,0,0}; int dl[]={0,0,-1,1}; int sum,n,m; void dfs(int a,int b) { if(a==n&&b==m)//走到终点 { sum++; return; } for(int i=0;i<4;i++) { int da=a+dh[i],db=b+dl[i]; if(da>=1&&da<=n&&db>=1&&db<=m&&!maps[da][db]&&!vis[da][db])//满足条件 { vis[da][db]=1;//标记 dfs(da,db);//DFS vis[da][db]=0;//回溯 } } } int main() { int T; cin>>T; while(T--) { memset(maps,0,sizeof(maps)); memset(vis,0,sizeof(vis)); sum=0; cin>>n>>m; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) cin>>maps[i][j]; } vis[1][1]=1; dfs(1,1); cout<<sum<<endl; } return 0; }
第一行包含两个整数n,m(分别代表n个隘口,这些隘口之间有m个通道)。
#include <iostream>//不要把回溯法和DFS搞混了!!! #include<cstring> #include<cstdio> using namespace std; const int maxn=1000; int n,m; bool vis[maxn]; bool sign; struct Node { int date; Node *next; }; Node *node[maxn]; void dfs(int a) { if(a==1)//到达即可 { sign=true; return; } vis[a]=1; Node *t=node[a]; while(t) { if(!vis[t->date]) dfs(t->date); t=t->next; } } int main() { while(scanf("%d%d",&n,&m)!=EOF) { sign=false; memset(node,0,sizeof(node)); memset(vis,0,sizeof(vis)); for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); if(node[a]==NULL) { node[a]=new Node; node[a]->date=b; node[a]->next=NULL; } else { Node *p=new Node; p->date=b; p->next=node[a]->next; node[a]->next=p; } } Node *t=node[n]; vis[n]=1; dfs(n); if(sign) printf("YES\n"); else printf("NO\n"); } return 0; }
例题3:小鑫的女朋友被魔王抢走了!
魔王留给小鑫一张n*m大的表,上面有各种各样的颜色,用A-Z这26个字母来表示。魔王留给他一个任务,如果小鑫可以在这张表中找出任意一个长度大于1的环,并且这个环的颜色是相同的,魔王就把小鑫的女朋友还给他。为了从魔王手中夺回他的女朋友,小鑫请你帮忙,你能帮帮他吗?
每组的第一行有两个整数n,m。代表表的大小。
接下来是由A-Z的一些字母所构成的n行m列的表。
如果可以救回他的女朋友,输出Yes,否则输出No
#include <iostream> #include<cstring> #include<cstdio> using namespace std; char maps[201][201]; int vis[201][201];//不用再回溯为0,因为深度搜索尽力的搜索任何一个可能的联通块,如果在这个搜索里不行,那么下一个肯定也不行!!! int dx[]= {-1,1,0,0}; int dy[]= {0,0,-1,1}; int notd[]={1,0,3,2};//不能走标记,走dx[0]的时候下一个点不能走dx[1],以此类推; int n,m; bool sign; void dfs(int a,int b,int d) { if(sign) return; for(int i=0;i<4;i++) { if(i==d)continue; int mx=a+dx[i]; int my=b+dy[i]; if(mx>=1&&mx<=n&&my>=1&&my<=m&&maps[mx][my]==maps[a][b]) { if(vis[mx][my])//找到某个已经走过的点,形成了环! { sign=true; return; } vis[mx][my]=1; dfs(mx,my,notd[i]);//不能走notd[i] } } } int main() { while(cin>>n>>m) { sign=false; memset(maps,0,sizeof(maps)); memset(vis,0,sizeof(vis)); for(int i=1; i<=n; i++) { for(int j=1; j<=m; j++) cin>>maps[i][j]; } for(int i=1; i<=n; i++) { for(int j=1; j<=m; j++) { if(!vis[i][j]) { vis[i][j]=1; dfs(i,j,-1); } if(sign) break; } if(sign) break; } if(sign) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; }<2>广度搜索优先(BFS)
它是一个分层搜索的过程和二叉树的层次遍历十分相似,它也需要一个队列以保持遍历过的顶点顺序,以便按出队的顺序再去访问这些顶点的邻接顶点。它是按照以某点为中心向周围扩散的顺序,因此他第一个碰到目的地的长度就是最短路!!
基本实现思想:
(1)顶点v入队列。
(2)当队列非空时则继续执行,否则算法结束。
(3)出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。
(4)查找顶点v的第一个邻接顶点col。
(5)若v的邻接顶点col未被访问过的,则col入队列。
(6)继续查找顶点v的另一个新的邻接顶点col,转到步骤(5)。
直到顶点全部处理完,队列为空转到(2)
(1)初始化队列Q;visited[n]=0;
(2)访问顶点v;visited[v]=1;顶点v入队列Q;
(3) while(队列Q非空)
v=队列Q的对头元素出队;
w=顶点v的第一个邻接点;
while(w存在)
如果w未访问,则访问顶点w;
visited[w]=1;
顶点w入队列Q;
w=顶点v的下一个邻接点。
例题1:给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列。(同一个结点的同层邻接点,节点编号小的优先遍历)输入第一行为整数n(0< n <100),表示数据的组数。
对于每组数据,第一行是三个整数k,m,t(0<k<100,0<m<(k-1)*k/2,0< t<k),表示有m条边,k个顶点,t为遍历的起始顶点。
下面的m行,每行是空格隔开的两个整数u,v,表示一条连接u,v顶点的无向边。(请使用邻接矩阵)
#include <iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; const int maxn=1000; int G[maxn][maxn]; int vis[maxn]; int n,m,t; void BFS(int a) { vis[a]=1; cout<<a; queue<int> que; que.push(a); while(!que.empty()) { int k=que.front(); que.pop(); for(int i=0;i<n;i++) { if(G[k][i]&&!vis[i]) { cout<<" "<<i; vis[i]=1; que.push(i); } } } cout<<endl; } int main() { int T; cin>>T; while(T--) { cin>>n>>m>>t; memset(G,0,sizeof(G)); memset(vis,0,sizeof(vis)); for(int i=0;i<m;i++) { int a,b; cin>>a>>b; G[a][b]=G[b][a]=1; } BFS(t); } return 0; }
对于每组数据,第一行是三个整数k,m,t(0<k<100,0<m<(k-1)*k/2,0< t<k),表示有m条边,k个顶点,t为遍历的起始顶点。
下面的m行,每行是空格隔开的两个整数u,v,表示一条连接u,v顶点的无向边。
#include <iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; const int maxn=1010; struct Node { int date; Node *next; }; Node *node[maxn]; int vis[maxn]; int k,m,t; void BFS(int a) { vis[a]=1; cout<<a; queue<int> que; que.push(a); for(int i=0;i<m;i++)//每个点都冒泡排序!!该点的联通点由小到大排列(这里差点忘了冒泡排序怎么写QAQ,快排用多了要复习巩固了QAQ) { for(Node *p=node[i];p!=NULL;p=p->next) { for(Node *q=p->next;q!=NULL;q=q->next) { if(p->date>q->date) { int temp=p->date; p->date=q->date; q->date=temp; } } } } //cout<<"yes"<<endl; while(!que.empty())//BFS { int u=que.front(); que.pop(); Node *mm=node[u]; while(mm) { if(!vis[mm->date]) { vis[mm->date]=1; cout<<" "<<mm->date; que.push(mm->date); } mm=mm->next; } } } int main() { int T; cin>>T; while(T--) { memset(node,0,sizeof(node)); memset(vis,0,sizeof(vis)); cin>>k>>m>>t; for(int i=1;i<=m;i++) { int a,b; cin>>a>>b; if(node[a]==NULL)//建立双向邻接表 { node[a]=new Node; node[a]->date=b; node[a]->next=NULL; } else if(node[a]!=NULL) { Node *p=new Node; p->date=b; p->next=node[a]->next; node[a]->next=p; } if(node[b]==NULL) { node[b]=new Node; node[b]->date=a; node[b]->next=NULL; } else if(node[b]!=NULL) { Node *p=new Node; p->date=a; p->next=node[b]->next; node[b]->next=p; } } BFS(t); cout<<endl; } return 0; }例题3:在古老的魔兽传说中,有两个军团,一个叫天灾,一个叫近卫。在他们所在的地域,有n个隘口,编号为1..n,某些隘口之间是有通道连接的。其中近卫军团在1号隘口,天灾军团在n号隘口。某一天,天灾军团的领袖巫妖王决定派兵攻打近卫军团,天灾军团的部队如此庞大,甚至可以填江过河。但是巫妖王不想付出不必要的代价,他想知道在不修建任何通道的前提下,部队是否可以通过隘口及其相关通道到达近卫军团展开攻击;如果可以的话,最少需要经过多少通道。由于n的值比较大(n<=1000),于是巫妖王找到了擅长编程的你 =_=,请你帮他解决这个问题,否则就把你吃掉变成他的魔法。为了拯救自己,赶紧想办法吧。
输入包含多组,每组格式如下。
第一行包含两个整数n,m(分别代表n个隘口,这些隘口之间有m个通道)。
#include <iostream> #include<cstring> #include<queue> using namespace std; const int maxn=1010; int G[maxn][maxn]; int vis[maxn]; int n,m; struct Node { int step; }; Node per[maxn];//存储步数! bool sign; bool dfs(int a) { vis[a]=1; per[a].step=0; queue<int> num; num.push(a); while(!num.empty()) { int u=num.front(); num.pop(); for(int i=1;i<=n;i++) { if(G[u][i]&&i==1&&!vis[i]) { per[1].step=per[u].step+1; return true; } else if(G[u][i]&&!vis[i]) { num.push(i); vis[i]=1; per[i].step=per[u].step+1; } } } return false; } int main() { while(cin>>n>>m) { sign=false; memset(per,0,sizeof(per)); memset(G,0,sizeof(G)); memset(vis,0,sizeof(vis)); for(int i=0;i<m;i++) { int a,b; cin>>a>>b; G[a][b]=1; } if(dfs(n)) cout<<per[1].step<<endl; else cout<<"NO"<<endl; } return 0; }例题4:
X#Y
***
#*#
3 3
X#Y
*#*
#*#输出:4 -1
#include <iostream> #include<cstring> #include<cstdio> #include<string> #include<queue> using namespace std; char maps[16][16]; int vis[16][16]; int movex[]={-1,1,0,0}; int movey[]={0,0,-1,1}; int x1,y1,x2,y2,n,m; struct Node { int x,y,step; Node(int a,int b,int c):x(a),y(b),step(c) {} }; int dfs(int a,int b) { vis[a][b]=1; queue<Node> que; que.push(Node(a,b,0)); while(!que.empty()) { Node u=que.front(); que.pop(); for(int i=0;i<4;i++) { int dx=u.x+movex[i]; int dy=u.y+movey[i];//移动,相当于找图的联通部分 if(dx==x2&&dy==y2&&!vis[dx][dy])//目的地 { return u.step+1; } else if(dx>=1&&dx<=m&&dy>=1&&dy<=n&&!vis[dx][dy]&&maps[dx][dy]=='*')//否则继续走BFS { vis[dx][dy]=1; que.push(Node(dx,dy,u.step+1)); } } } return -1;//都没有则返回-1; } int main() { while(cin>>m>>n) { memset(maps,0,sizeof(maps)); memset(vis,0,sizeof(vis)); for(int i=1; i<=m; i++) { for(int j=1;j<=n;j++) { cin>>maps[i][j]; if(maps[i][j]=='X')//记录下起始位置和目的地位置 { x1=i;y1=j; } else if(maps[i][j]=='Y') { x2=i;y2=j; } } } int k=dfs(x1,y1); cout<<k<<endl; } return 0; }
第一行是三个正整数n,m,q。分别代表楼的总层数,给定的m条信息和q次查询。
接下来的m行,每行的第一个整数pos代表这是第pos层的电梯,第二个数代表从这一层可以去的楼层总共有num个,之后的num个数字代表从第pos层代表可以去的楼层。
最后的q行,每行一个整数代表小鑫想去的楼层号码。
1<=m,pos,num<=n<=200
1<=q<=20
#include <iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; int G[2001][2001]; int vis[2001]; int n,m,q,p; struct Node { int x; int step; Node(int aa,int bb):x(aa),step(bb) {} }; int BFS(int a) { vis[a]=1; queue<Node>que; que.push(Node(a,0));//对于1->1层问题,应该输出1,错在这里!原程序输出-1; while(!que.empty()) { Node u=que.front(); que.pop(); if(u.x==p) return u.step+1; for(int i=1;i<=n;i++) { //if(G[u.x][i]&&!vis[i]&&i==p)//原程序这样,若输入p=1的话,原本就在1层,1->1应为1但这里输出-1因为G[1][1]=0! //return u.step+1; if(G[u.x][i]&&!vis[i]) { que.push(Node(i,u.step+1)); vis[i]=1; } } } return -1; } int main() { while(cin>>n>>m>>q) { memset(G,0,sizeof(G)); for(int i=1;i<=m;i++) { int a,num,t; cin>>a>>num; for(int j=1;j<=num;j++) { cin>>t; G[a][t]=1; } } while(q--) { memset(vis,0,sizeof(vis)); cin>>p; int k=BFS(1); cout<<k<<endl; } } return 0; }
输入第一行给出3个正整数
n
、
m
和
k
,其中
n
(
≤10000)是总的山头数(于是假设每个山头从1到
n
编号)。接下来的
m
行,每行给出2个不超过
n
的正整数,数字间用空格分开,分别代表可以听到彼此的两个山头的编号。这里保证每一对山头只被输入一次,不会有重复的关系输入。最后一行给出
k
(
≤10)个不超过
n
的正整数,数字间用空格分开,代表需要查询的山头的编号。
#include <iostream> #include<bits/stdc++.h> using namespace std; vector<int> line[10010];//用vector建立邻接表 bool vis[10010];//标记走过 int n,m,k; struct Node { int id; int step; Node(int a,int b):id(a),step(b) {} }; int BFS(int a) { if(line[a].empty()) return 0; vis[a]=1; int maxn=0; int dd=10001; queue<Node> que; que.push(Node(a,0)); while(!que.empty()) { Node u=que.front(); que.pop(); if(u.step>maxn)//判断 { dd=u.id; maxn=u.step; } else if(u.step==maxn) dd=dd<u.id?dd:u.id; for(int i=0; i<line[u.id].size(); i++) { if(!vis[line[u.id][i]]) { que.push(Node(line[u.id][i],u.step+1)); vis[line[u.id][i]]=1; } } } return dd; } int main() { cin>>n>>m>>k; while(m--) { int a,b; cin>>a>>b; line[a].push_back(b); line[b].push_back(a); } while(k--) { memset(vis,0,sizeof(vis)); int num; cin>>num; int mm=BFS(num); cout<<mm<<endl; } return 0; }
拓扑排序对应施工的流程图具有特别重要的作用,它可以决定哪些子工程必须要先执行,哪些子工程要在某些工程执行后才可以执行。为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行。通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。
一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行(对于数据流来说就是死循环)。在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。
- 在有向图中选一个没有前驱的顶点并且输出
- 从图中删除该顶点和所有以它为尾的弧(白话就是:删除所有和它有关的边)
- 重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。
基于邻接矩阵:
#include <iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int maxn=101; int G[maxn][maxn];//节点是否联通 int flow[maxn];//记录节点的入度 int n,m; vector<int> num;//保存结果顺序 void dfs() { int counter=0;//用来判断有无环路! queue<int> q; for(int i=1; i<=n; i++) { if(flow[i]==0)//最开始从入度为零出发,找出第一批无前驱点放入队列 q.push(i); } while(!q.empty()) { int u=q.front(); q.pop(); num.push_back(u); for(int j=1; j<=n; j++) { if(G[u][j]&&) { flow[j]--; if(flow[j]==0)//如果入度为零了,也要存入queue(可能还要加个vis标记) q.push(j); } } ++counter; } cout<<counter<<endl; if(counter!=n)//不=节点个数即有环路,一般来说大于n! cout<<"´æÔÚ»·£¡"<<endl; } int main() { while(cin>>n>>m&&(n||m)) { num.clear(); memset(G,0,sizeof(G)); memset(flow,0,sizeof(flow)); while(m--) { int a,b; cin>>a>>b; G[a][b]=1; flow[b]++;//入度增加 } dfs(); for(int i=0; i<num.size(); i++) { if(i==0) cout<<num[i]; else cout<<" "<<num[i]; } cout<<endl; } return 0; }基于邻接表:
#include <iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; const int maxn=10000; struct Node { int val; Node *next; }; Node *node[maxn]; int n,m; int connect[maxn]; void print_ans() { vector<int>ver; queue<int>q; int counter=0; for(int j=1;j<=n;j++) { if(connect[j]==0) q.push(j); } while(!q.empty()) { int u=q.front(); q.pop(); ver.push_back(u); counter++; Node *root=node[u]; while(root) { connect[root->val]--; if(connect[root->val]==0) { q.push(root->val); } root=root->next; } } if(counter!=n) cout<<"there is a yuan!\n"; else { for(int i=0;i<ver.size();i++) cout<<ver[i]<<" "; cout<<endl; } } int main() { memset(connect,0,sizeof(connect)); memset(node,0,sizeof(node)); while(cin>>n>>m) { for(int i=0;i<m;i++) { int a,b; cin>>a>>b; connect[b]++; Node *p; if(node[a]==NULL)//建表 { node[a]=new Node; node[a]->val=b; node[a]->next=NULL; } else { p=new Node; p->val=b; p->next=node[a]->next; node[a]->next=p; } } print_ans(); } return 0; }
核心思路:用vis数组来标记节点状态:0表示未访问,1表示已经访问,-1表示正在访问。如某节点递归过程中发现子节点状态为-1,则说明图中有环!!
基于邻接矩阵:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<stack>
using namespace std;
const int maxn=1000;
int G[maxn][maxn];
int vis[maxn];
int n,m;
stack<int> num;
bool dfs(int a)
{
vis[a]=-1;
for(int j=1;j<=n;j++)
{
if(G[a][j])
{
if(vis[j]==-1)//环
return false;
else if(!vis[j]&&!dfs(j))//若未访问过且访问后是-1还是有环
return false;
}
}
vis[a]=1;//标记已经访问
num.push(a);//栈压入元素放在末尾!由于加入顶点到集合中的时机是在dfs方法即将退出之时,(若用数组,则是num[--t]=a;t=n;)
//而dfs方法本身是个递归方法,
//仅仅要当前顶点还存在边指向其他不论什么顶点,
//它就会递归调用dfs方法,而不会退出。
//因此,退出dfs方法,意味着当前顶点没有指向其他顶点的边了
//,即当前顶点是一条路径上的最后一个顶点。
//换句话说其实就是此时该顶点出度为0了
return true;
}
int main()
{
while(cin>>n>>m)
{
memset(G,0,sizeof(G));
memset(vis,0,sizeof(vis));
for(int i=0;i<m;i++)
{
int a,b;
cin>>a>>b;
G[a][b]=1;
}
bool sign=true;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
if(!dfs(i))
{
sign=false;
cout<<"there is a huan!\n";
break;
}
}
}
if(sign)
{
while(!num.empty())
{
cout<<num.top()<<" ";
num.pop();
}
cout<<endl;
}
}
return 0;
}
基于邻接表:
#include <iostream> #include<stack> #include<cstring> #include<cstdio> using namespace std; const int maxn=1000; struct Node { int val; Node *next; }; Node *node[maxn]; int vis[maxn]; int n,m; stack<int> num; void dfs(int a) { vis[a]=1; Node *t=node[a]; while(t) { if(vis[t->val]==0) { dfs(t->val); } t=t->next; } num.push(a); } void print_ans() { for(int i=1;i<=n;i++) { if(!vis[i]) { dfs(i); } } while(!num.empty()) { cout<<num.top()<<" "; num.pop(); } cout<<endl; } int main() { while(cin>>n>>m) { memset(node,0,sizeof(node)); memset(vis,0,sizeof(vis)); for(int i=0;i<m;i++) { int a,b; cin>>a>>b; if(node[a]==NULL) { node[a]=new Node; node[a]->val=b; node[a]->next=NULL; } else { Node *p=new Node; p->val=b; p->next=node[a]->next; node[a]->next=p; } } print_ans(); } return 0; }