[题解]牛跑步
前言:
哈哈,今晚再写一篇题解
并查集双杀
题目描述
新牛到部队,Farmer John要求它们每天早上搞晨跑,从A农场跑到B农场。从A农场到B农场中有n-2个路口,分别标上号,A农场为1号,B农场为n号,路口分别为2...n-1号,从A农场到B农场有很多条路径可以到达,而Farmer John发现有的路口是必须经过的,即每条路径都经过的路口,Farmer John要把它们记录下来,这样Farmer John就可以先到那个路口,观察新牛们有没有偷懒,而你的任务就是找出所有必经路口。
输入输出格式
输入格式:
第一行,两个用空格隔开的整数n(3≤n≤2000)和e(1≤e≤8000)。
接下来从第2到第e+1行,每行两个用空格隔开的整数p和q,表示路口p和q之间有路径直达。
输入数据保证必经路口一定存在,并且每个路口都和A农场、B农场相连通。
输出格式:
第一行,一个整数m,表示必经路口的数目。
第二行,按照从小到大的顺序依次输出每个必经路口的编号,每两个数之间用一个空格隔开。
注意:不包括起点和终点。
输入输出样例
输入样例:
6 6 1 2 2 4 2 3 3 5 4 5 5 6
输出样例:
2 2 5
题目分析:
我们假如用并查集的思想,枚举每一个节点,清空并查集,将除该割点的相关边所连点加入并查集,如果1与n都在此并查集(1、n祖先为一个节点)证明不需要这个割点也可以到达最后,那么这个点就不为必经点。
上面的分析如果没有看懂一定要多看几次,再拿笔推一推,这道题就明了了。
代码:
1 #include<iostream> 2 #include<fstream> 3 #include<cstring> 4 using namespace std; 5 6 const int Max_N=2e3+5; 7 const int Max_E=1.6e4+5; 8 9 int n,e,tot,cnt; 10 int Next[Max_E],root[Max_N],to[Max_E]; 11 12 int Fa[Max_N],res[Max_N]; 13 14 void Add_Edge(int u,int v) 15 { 16 // 链式前向星 17 Next[++tot]=root[u]; 18 root[u]=tot; 19 to[tot]=v; 20 return ; 21 } 22 void init() 23 { 24 // 初始化并查集 25 register int i; 26 for(i=1;i<=n;i++) 27 Fa[i]=i; 28 return ; 29 } 30 int Find(int p) 31 { 32 // 查找节点祖先 33 if(Fa[p]==p) 34 return p; 35 return Fa[p]=Find(Fa[p]); 36 } 37 void megre(int l,int r) 38 { 39 // 合并两点 40 Fa[Find(l)]=Fa[Find(r)]; 41 return ; 42 } 43 void Ge(int p) 44 { 45 // 枚举割点 46 register int i,j; 47 for(i=1;i<=n;i++) 48 if(i^p) 49 for(j=root[i];j^0;j=Next[j]) 50 if(to[j]^p) 51 megre(i,to[j]); 52 return ; 53 } 54 int main() 55 { 56 scanf("%d%d",&n,&e); 57 register int i; 58 int x,y; 59 for(i=1;i<=e;i++){ 60 scanf("%d%d",&x,&y); 61 Add_Edge(x,y); 62 Add_Edge(y,x); 63 // 此图为无向图 64 } 65 int Ans=0; 66 for(i=2;i<n;i++){ 67 init(); 68 Ge(i); 69 if(Find(1)^Find(n)){ 70 // 不需要该点依然合格 71 Ans++; 72 res[++cnt]=i; 73 } 74 } 75 printf("%d\n",Ans); 76 for(i=1;i<=cnt;i++){ 77 if(i>1) 78 printf(" "); 79 printf("%d",res[i]); 80 } 81 return 0; 82 }
写在最后的话:
题解仅供思路,要想成为 dalao ,请学会并尽量会做到教他人甚至自己写题解。
博主(目前)是一名初二蒟蒻,如有问题还请大家指出,一起交流学习!
Happy every day! ——2019.4.11