题目大意:
给出一个森林,首先判断这个森林是否合法,然后求出这个森林的最大深度和最大宽度。
不合法的意思是:有环或者存在多个结点指向同一个结点的情况。
最大深度的定义是:所有树的深度的最大值,根结点的深度为0。
最大宽度的定义是:同一层的结点总数为该层的宽度,所有层的最大宽度为森林的最大宽度。
算法实现:
用拓扑排序的方法判断有向图是否有环,然后用dfs求出最大深度和最大宽度,同时判断是否有多个结点指向同一个结点的情况(被指向的结点会被访问多次)。
具体代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<stack>
#define MAXN 101
using namespace std;
struct ArcNode
{
int adjvex;
ArcNode *next;
};
struct VNode
{
ArcNode *first;
};
struct Graph
{
int vexnum;
VNode v[MAXN];
};
int max_depth;
int in[MAXN],vis[MAXN],node_num[MAXN];
Graph g;
bool dfs(int cur,int level)
{
if(vis[cur]) return 0;
vis[cur]=1;
node_num[level]++;
if(g.v[cur].first==NULL)
{
if(level>max_depth)
max_depth=level;
return 1;
}
ArcNode *p;
for(p=g.v[cur].first;p;p=p->next)
if(!dfs(p->adjvex,level+1))
return 0;
return 1;
}
/**利用拓扑排序判断有向图是否有环*/
bool topological_sort(Graph &g,int *in)
{
int *indegree=(int *)malloc((g.vexnum+1)*sizeof(int));
memcpy(indegree,in,(g.vexnum+1)*sizeof(int));
stack<int> s;
for(int i=1;i<=g.vexnum;i++)
if(indegree[i]==0)
s.push(i);
int cnt=0;
while(!s.empty())
{
int i=s.top();
s.pop();
cnt++;
ArcNode *p;
for(p=g.v[i].first;p;p=p->next)
{
int k=p->adjvex;
indegree[k]--;
if(indegree[k]==0)
{
s.push(k);
}
}
}
free(indegree);
if(cnt<g.vexnum) return 0;
return 1;
}
int main()
{
int n,m;
while(cin>>n>>m&&n>0)
{
g.vexnum=n;
for(int i=1;i<=n;i++)
g.v[i].first=NULL;
memset(in,0,sizeof(in));
for(int i=1;i<=m;i++)
{
int v1,v2;
cin>>v1>>v2;
in[v2]++;
ArcNode *tmp=new ArcNode();
tmp->adjvex=v2;
tmp->next=g.v[v1].first;
g.v[v1].first=tmp;
}
if(!topological_sort(g,in))
{
cout<<"INVALID"<<endl;
continue;
}
bool invalid=0;
memset(vis,0,sizeof(vis));
max_depth=0;
memset(node_num,0,sizeof(node_num));
for(int i=1;i<=n;i++)
if(in[i]==0)
{
if(!dfs(i,0))
{
invalid=1;
break;
}
}
if(invalid)
{
cout<<"INVALID"<<endl;
continue;
}
int max_width=0;
for(int i=0;i<MAXN;i++)
if(node_num[i]>max_width)
max_width=node_num[i];
cout<<max_depth<<" "<<max_width<<endl;
}
return 0;
}
收获:用拓扑排序判断有向图是否有环的方法是判断是否所有结点都被排序,如果存在没有被排序的结点,那么就会有环。这个一个比较保险的方法。如果使用对每个结点都进行dfs,根据是否存在被重复访问的结点判断是否有环的方法,是不正确的。因为我们不知道结点的访问顺序,从而会出现孩子结点被重复访问的情况。