机试练习05:poj1308——Is It A Tree?
一、题解方法
这道题还是应用的并查集的方法。
考虑不是一棵树的情况:
- 形成环,无根,即每个节点的入度为0。
- 有多个根,即形成森林。
- 节点有多个父节点,即节点的入度>1。
用并查集求解:
- 判断当前有向边的两端点是否在同一个集合内,若在,则一定存在环,则不存在这样的一棵树;若不在,进行其他判断。
- 对于多个父节点的判断,可以统计每个节点的入度,若入度>1,则不存在这样的一棵树;若不在,进行其他判断。
- 设置两个维护的变量,cnt保存节点总数,ans保存所有有效边的数量,即有向边两端点不在同一集合内。(参考上道并查集解决连通的问题)
若ans = cnt - 1,则说明所有结点连接到一个根节点上,是一棵树;若不相等,则不是一棵树。
解题过程中的问题:
- 最初,判断是否为森林的方法是利用set,即判断集合里所有节点是否具有同一个根;判断是否有多个父节点时利用map,即输入当前有向边后,判断map中是否已存在<当前节点, 父节点>对。结果报MLE,弃用STL。
- 改为如上方法,用两个数组来维护每个节点的访问情况和入度情况,最后ac。
二、题解代码
1 #include <iostream> 2 #include "stdio.h" 3 #include "stdlib.h" 4 #include "string.h" 5 #include <set> 6 #include <map> 7 8 #define MAX 500000 9 10 using namespace std; 11 12 int root[MAX]; 13 int num[MAX]; 14 bool vi[MAX]; 15 16 void init() 17 { 18 for(int i = 0; i < MAX; i++) 19 root[i] = i; 20 } 21 22 int find(int x) 23 { 24 if(x != root[x]) 25 root[x] = find(root[x]); 26 return root[x]; 27 } 28 29 int main() 30 { 31 int count = 0; 32 int start, end; 33 int ans, cnt; 34 35 init(); 36 37 while(scanf("%d%d", &start,&end) != EOF && (start>=0 && end>=0)) 38 { 39 ++count; 40 memset(num,0,sizeof(num)); 41 memset(vi,0,sizeof(vi)); 42 bool flag = true; 43 ans = 0, cnt = 0; 44 if(start == 0 && end == 0) 45 { 46 printf("Case %d is a tree.\n", count); 47 continue; 48 } 49 50 while(!(start == 0 && end == 0)) 51 { 52 if(find(start) != find(end)) 53 { 54 root[end] = start; 55 ans++; 56 } 57 else 58 { 59 flag = false; 60 } 61 62 if(!vi[start]){ 63 cnt++; 64 vi[start]=1; 65 } 66 if(!vi[end]){ 67 cnt++; 68 vi[end]=1; 69 } 70 num[end]++; 71 if(num[end] > 1) 72 flag = false; 73 scanf("%d%d", &start,&end); 74 75 } 76 77 if(flag == true && ans == cnt - 1) 78 printf("Case %d is a tree.\n", count); 79 else 80 printf("Case %d is not a tree.\n", count); 81 82 init(); 83 } 84 return 0; 85 }