1021. Deepest Root (25)

来源:互联网 发布:python 游戏编程 pdf 编辑:程序博客网 时间:2024/06/05 22:52

1021. Deepest Root (25)

时间限制
1500 ms
内存限制
65536 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

A graph which is connected and acyclic can be considered a tree. The height of the tree depends on the selected root. Now you are supposed to find the root that results in a highest tree. Such a root is called the deepest root.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (<=10000) which is the number of nodes, and hence the nodes are numbered from 1 to N. Then N-1 lines follow, each describes an edge by given the two adjacent nodes' numbers.

Output Specification:

For each test case, print each of the deepest roots in a line. If such a root is not unique, print them in increasing order of their numbers. In case that the given graph is not a tree, print "Error: K components" where K is the number of connected components in the graph.

Sample Input 1:
51 21 31 42 5
Sample Output 1:
345
Sample Input 2:
51 31 42 53 4
Sample Output 2:
Error: 2 components

题目类型:dfs/并查集

这一题搞明白了,图的深度遍历dfs以及递归也就清楚了。

*********************这道题要注意一点要用邻接表存储图,如果用邻接矩阵会超时(稀疏图)********************

题目大意:给出n个结点(1~n)之间的n条边,问是否能构成一棵树,如果不能构成则输出它有的连通分量个数,如果能构成一棵树,输出能构成最深的树的高度时,树的根结点。如果有多个,按照从小到大输出。

(1)我开始用的是并查集来求连通分量的个数,再用dfs求每个节点为根节点的数的深度。并查集采用地递归也没用,但是,提交最后一个案例给我报时间超时。一开始edges[][]和visit[]用的是int类型,提交会报内存不够的错误,所以都换成了bool类型。我的代码:

#include<iostream>#include<cstring>using namespace std;const int maxsize=10001;int node[maxsize];bool edges[maxsize][maxsize];bool visit[maxsize]; int deep[maxsize];int onenodedeep=0;int maxlevel=0;int n;int findroot(int a){if(node[a]!=a)//这里用if判断,而不是while(***很重要***) node[a]=findroot(node[a]);return node[a];}int dfs(int v,int level){visit[v]=true;for(int i=1;i<=n;++i)//***这里每个节点都要遍历N个,复杂度非常高(这才是超时的根本) {if(visit[i]==0 && edges[v][i]==true){onenodedeep=level>onenodedeep?level:onenodedeep;dfs(i,level+1);visit[i]=false;//(***很重要***)}}return onenodedeep;}int main(){//freopen("in.txt","r",stdin);cin>>n;for(int i=1;i<=n;++i)node[i]=i;int components=n;int a,b;for(int i=0;i<n-1;++i){cin>>a>>b;node[i+1]=i+1;edges[a][b]=edges[b][a]=true;int roota=findroot(a);int rootb=findroot(b);if(roota!=rootb){--components;node[roota]=rootb;}}if(components>1)cout<<"Error: "<<components<<" components";else{for(int i=1;i<=n;++i){onenodedeep=0;deep[i]=dfs(i,0);if(deep[i]>maxlevel)maxlevel=deep[i];visit[i]=false;}for(int i=1;i<=n;++i)if(deep[i]==maxlevel)cout<<i<<endl;}return 0;}


分析:其实主要是在用dfs进行深度计算的时候,每个节点都是在循环遍历n那个节点,导致复杂度急剧加大,有o(n^n)。因此我有采用可了二个dfs求,然而并没有什么卵用,因为复杂度并没有降低。

(2)于是我在计算连通分量的时候同样采用dfs深度遍历,结果一样是有一个测试案例超时。(还是没有解决dfs内部内各节点都进行访问判断的复杂度降低问题)

我的代码如下:

#include<iostream>#include<cstring>using namespace std;const int maxsize=10001;bool edges[maxsize][maxsize];bool visit[maxsize]; int deep[maxsize];int onenodedeep=0;int maxlevel=0;int n;int dfs(int v,int level){visit[v]=1;for(int i=1;i<=n;++i){if(visit[i]==0 && edges[v][i]==1){onenodedeep=level>onenodedeep?level:onenodedeep;dfs(i,level+1);visit[i]=false;//(重要) }}return onenodedeep;}void dfs_con(int v)//深度遍历求连通分量的个数 {visit[v]=1;for(int i=1;i<=n;++i)//***这里每个节点都要遍历N个,复杂度非常高(这才是超时的根本) {if(visit[i]==0 && edges[v][i]==1)dfs_con(i);}}int main(){//freopen("in.txt","r",stdin);cin>>n;int components=0;int a,b;for(int i=0;i<n-1;++i){scanf("%d %d",&a,&b);edges[a][b]=edges[b][a]=true;}for(int i=1;i<=n;++i){if(visit[i]==0){++components;dfs_con(i);}}if(components>1)cout<<"Error: "<<components<<" components";else{memset(visit,0,sizeof(visit));for(int i=1;i<=n;++i){onenodedeep=0;deep[i]=dfs(i,0);if(deep[i]>maxlevel)maxlevel=deep[i];visit[i]=false;//消除当前节点的访问 }for(int i=1;i<=n;++i)if(deep[i]==maxlevel)cout<<i<<endl;}return 0;}
提交结果:



(3)看了一下比人的代码,然后写了改进之后的代码,提交通过。

思路大致如下:设置二维的vector向量,把每个节点所连的边push_back()到相应节点的行里,每次递归只对该行所连节点进行深度递归,剪枝去掉非相连接点的访问判断问题,大大的减少了复杂度。(改进之后可以用二个dfs或者dfs+并查集)

我改进的dfs+dfs代码如下:

#include<iostream>#include<cstring>#include<vector> using namespace std;const int maxsize=10001;bool visit[maxsize]; int deep[maxsize];int onenodedeep=0;int maxlevel=0;int n;vector< vector<int> > edges;int dfs(int v,int level){if(edges[v].size()==0 || visit[v]==1)//结束递归的标志 return 1;//这里的返回值不影响要改变的全局变量onenodedeep的值 visit[v]=1;for(int i=0;i<edges[v].size();++i){if(visit[edges[v][i]]==0){onenodedeep=level>onenodedeep?level:onenodedeep;dfs(edges[v][i],level+1);visit[edges[v][i]]=false;//(重要) }}return onenodedeep;}void dfs_con(int v)//dfs求连通分量的个数 {if(edges[v].size()==0 || visit[v]==1)//一条递归路径结束的标志 return ;visit[v]=1;for(int i=0;i<edges[v].size();++i){if(visit[edges[v][i]]==0)// 不再是判断visit[i]==0(**重要**) dfs_con(edges[v][i]);}}int main(){//freopen("in.txt","r",stdin);cin>>n;edges.resize(n+1);int components=0;int a,b;for(int i=1;i<=n-1;++i){scanf("%d %d",&a,&b);edges[a].push_back(b);edges[b].push_back(a);}for(int i=1;i<=n;++i){if(visit[i]==0){++components;dfs_con(i);}}if(components>1)cout<<"Error: "<<components<<" components";else{memset(visit,0,sizeof(visit));for(int i=1;i<=n;++i){onenodedeep=0;deep[i]=dfs(i,0);if(deep[i]>maxlevel)maxlevel=deep[i];visit[i]=false;}for(int i=1;i<=n;++i)if(deep[i]==maxlevel)cout<<i<<endl;}return 0;}
提交结果:



(4)别人的代码,提交之后的复杂度是真的低

#include <cstdio>#include <vector>#include <set>#include <algorithm>using namespace std;int n, maxheight = 0;vector<vector<int>> v;bool visit[10010];set<int> s;vector<int> temp;void dfs(int node, int height) {    if(height > maxheight) {        temp.clear();        temp.push_back(node);        maxheight = height;    } else if(height == maxheight){        temp.push_back(node);    }    visit[node] = true;    for(int i = 0; i < v[node].size(); i++) {        if(visit[v[node][i]] == false)            dfs(v[node][i], height + 1);    }}int main() {    scanf("%d", &n);    v.resize(n + 1);    int a, b, cnt = 0;    for(int i = 0; i < n - 1; i++) {        scanf("%d%d", &a, &b);        v[a].push_back(b);        v[b].push_back(a);    }    int s1 = 0;    for(int i = 1; i <= n; i++) {        if(visit[i] == false) {            dfs(i, 1);            if(i == 1) {                for(int j = 0; j < temp.size(); j++) {                    s.insert(temp[j]);                    if(j == 0) s1 = temp[j];                }            }            cnt++;        }    }    if(cnt >= 2) {        printf("Error: %d components", cnt);    } else {        temp.clear();        maxheight = 0;        fill(visit, visit + 10010, false);        dfs(s1, 1);        for(int i = 0; i < temp.size(); i++)            s.insert(temp[i]);        for(set<int>::iterator it = s.begin(); it != s.end(); it++)            printf("%d\n", *it);            }    return 0;}

(5)
#include<iostream>  #include<cstdio>  #include<cstring>  #include<vector>  #include<algorithm>  using namespace std;  const int maxn=10001;  bool vis[maxn];  int a[maxn];  int n;  int cnt=0;  vector<int> ma[maxn];//邻接表  int d[maxn];//计算以这个点为树根时的最大深度  int dfs(int s)//dfs表示以S为起点所能达到的最大深度  {      int ans=0;      if(vis[s])return 0;//这个点已经被访问过,明显以这个点的最大深度是0      vis[s]=true;      int m=ma[s].size();      for(int i=0; i<m; i++)      {          if(!vis[ma[s][i]])          {              int tmp=dfs(ma[s][i]);//以当前点为起点访问能到达的最大的深度,也就是找出S的邻接点里的,深度,能达到的最大的              ans=max(ans,tmp);//ans记录下最大的深度即可          }      }      return ans+1;//能到这里说明以S点能到达的深度可以加一,也就是S相邻的顶点里的深度,最大值,加上S点,所以就是ans+1  }    void init(int n)//这是并查集的初始化  {      for(int i=0; i<=n; i++)          a[i]=i;  }  int find(int x)//并查集查找X的父亲,带路径压缩  {      if(a[x]!=x)          a[x]=find(a[x]);      return a[x];  }  void unio(int x,int y)//合并X,Y到一起  {      x=find(x);      y=find(y);      if(a[x]==a[y])return ;      a[x]=y;  }    int main()  {      int i,j,k,t;      // freopen("in.txt","r",stdin);      cin>>n;      init(n);      for(i=1; i<n; i++)      {          int s,e;          cin>>s>>e;          unio(s,e);          ma[s].push_back(e);          ma[e].push_back(s);      }        int sum=0;//判断连通分量的个数      for(i=1; i<=n; i++)      {          if(a[i]==i)sum++;      }      if(sum>1)      {          printf("Error: %d components\n",sum);          return 0;      }      else          for(i=1; i<=n; i++)              {                  memset(vis,0,sizeof(vis));                  d[i]=dfs(i);              }      int maxv=-1;int index=0;      for(i=1;i<=n;i++)if(d[i]>maxv){maxv=d[i];index=i;}      for(j=1;j<=n;j++)if(d[j]==d[index])          printf("%d\n",j);      }  
(6)

#include<iostream>  #include<string.h>  #include<vector>  using namespace std;  #define N 10001  vector<int> edge[N];  int deepest[N];  int visited[N];  int n;  int  dfs(int now_node){      if(visited[now_node]==1)          return 0;      visited[now_node]=1;      if(edge[now_node].size()==0)          return 1;      int i,maxdeep=0,tmp;      for(i=0;i<edge[now_node].size();i++){//find a deepest from this node          if(visited[edge[now_node][i]]==0){              tmp=dfs(edge[now_node][i]);              maxdeep=maxdeep>tmp?maxdeep:tmp;          }      }      return maxdeep+1;  }  int main()  {      int i,j;      cin>>n;      for(i=1;i<=n-1;i++){          int v1,v2;          cin>>v1>>v2;          edge[v1].push_back(v2);          edge[v2].push_back(v1);      }      memset(deepest,0,sizeof(deepest));      int max_deep=0;      int flag=0;      for(i=1;i<=n;i++){          if(0==flag)              memset(visited,0,sizeof(visited));          deepest[i]=dfs(i);          max_deep=max_deep>deepest[i]?max_deep:deepest[i];          for(j=i;j<=n;j++){//check if it is a connected graph              if(visited[j]==0){                  flag++;                  i=j-1;                  break;              }          }      }      if(flag>0){          cout<<"Error: "<<flag+1<<" components"<<endl;      }      else{          for(i=1;i<=n;i++){              if(deepest[i]==max_deep)                  cout<<i<<endl;          }      }      return 0;  }