关于昨天的并查集

来源:互联网 发布:mac怎么写java 编辑:程序博客网 时间:2024/06/07 01:18

最开始学习并查集的时候也是跟着人家的博客学习的,推荐一篇写的超级好的关于并查集的博客:并查集详解

关于并查集的使用,首先最基础的就是畅通工程那道题。代码也是关于并查集的模板。
把昨天的训练分下类的话(详见分类中的并查集):
一:ABDG 算是基础的套模板类型。
二:CE 找以i为根节点的子节点的个数
三:I J 需要记录节点到根节点的距离。
四:FH 有向图和无向图
模板的话大概就是三个函数


一,init()函数:用于初始化pre[]数组。

void init(int n){    for(int i=1;i<=n;i++)    {        pre[i]=i;    }}

二,find()函数,用于寻找父亲节点,经过路径压缩之后直接指向根节点。
有两种写法:
(1):while()循环

int find(int x){    int r=x;    while(r!=pre[r])    {        r=pre[r];    }//根节点    int i=x,j;    while(pre[i]!=r)    {        j=pre[i];        pre[i]=r;        i=j;    }//路径压缩    return r;}

(2):递归;//更推荐用递归写

int find(int x){    if(pre[x]==x)        return x;    return pre[x]=find(pre[x]);}//将根节点存入 

三:mix()函数,用于合并集合

void mix(int x,int y){    int fx=find(x);    int fy=find(y);    if(fx!=fy)    {        pre[fy]=fx;    }}

四种分类各说一个吧


一: A 畅通工程
畅通工程

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>#include<algorithm>using namespace std;int pre[11000];//记录父亲节点int t[11000];//最后的判断中使用t[]数组,用于统计根节点的个数int ans;int find(int x){    int r=x;    while(r!=pre[r])    {        r=pre[r];    }    int i=x,j;    while(pre[i]!=r)    {        j=pre[i];        pre[i]=r;        i=j;    }    return r;}void mix(int x,int y){    int fx=find(x);    int fy=find(y);    if(fx!=fy)    {        pre[fy]=fx;    }}int main(){    int n,m,a,b,i;    while(scanf("%d",&n)!=EOF&&n!=0)    {        scanf("%d",&m);        for(i=1;i<=n;i++)        {            pre[i]=i;        }//init()函数        for( i=1;i<=m;i++)        {            scanf("%d%d",&a,&b);            mix(a,b);        }        memset(t,0,sizeof(t));        for(i=1;i<=n;i++)        {            t[find(i)]=1;        }//找到i 的根节点。并在t[]数组中标记        for(ans=0,i=1;i<=n;i++)        {            if(t[i])                ans++;        }        printf("%d\n",ans-1);    }    return 0;}

二: C - The Suspects
C - The Suspects

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>#include<algorithm>#define MAX 100000+10using namespace std;int vis[110][110]; int pre[MAX];int rank[MAX];//用这个数组记录子节点的个数void init(int n){    for(int i=0;i<n;i++)    {        pre[i]=i;        rank[i]=1;//开始全赋值为1 只有自己一个    }}int find(int a){    if(pre[a]==a)        return a;    return pre[a] = find(pre[a]);}void mix(int x,int y){    x=find(x);    y=find(y);    if(x==y) return;      if(rank[x]>=rank[y])      {          pre[y]=x;          rank[x]=rank[x]+rank[y];  //根节点不同,把下级的子节点的个数加到上面去    }    else      {          pre[x]=y;          rank[y]=rank[y]+rank[x];      }  }int main(){    int n,m,before,next;    while(~scanf("%d%d",&n,&m))    {        if(n==0&&m==0)            break;        init(n);        for(int i = 0;i < m; i++)        {            int k;            scanf("%d%d",&k,&before);            for(int j=1;j<k;j++)            {                scanf("%d",&next);                mix(before,next);            }        }        cout<<rank[pre[0]]<<endl;    }    return 0;}

三: J - Work
J - Work

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>#include<algorithm>#include<vector>#define MAX 100+10using namespace std;int pre[MAX];  int sum[MAX];  int find(int x)  {      int r=x;      while(r!=pre[r])          r=pre[r];      int i=x,j;      while(i!=r)      {          j=pre[i];          sum[j]++;  //关键在于这一步,不是直接给根节点,而是一层一层加上去,统计距离。!        i=j;      }      return r;  }void init(int n){    for(int i=1;i<=n;i++)    {        pre[i]=i;    }}int main()  {      int n,m,i,j,x,y;      while(scanf("%d%d",&n,&m)!=EOF)      {          memset(sum,0,sizeof(sum));          int nn=n-1;          init(n);        while(nn--)          {              scanf("%d%d",&x,&y);              if(x!=y)                  pre[y]=x;          }          for(i=1;i<=n;i++)              find(i);          int cnt=0;          for(i=1;i<=n;i++)          {              if(sum[i]==m)                  cnt++;          }          printf("%d\n",cnt);      }      return 0;  }  

四:H - Is It A Tree?
A tree is a well-known data structure that is either empty (null, void, nothing) or is a set of one or more nodes connected by directed edges between nodes satisfying the following properties.
There is exactly one node, called the root, to which no directed edges point.

Every node except the root has exactly one edge pointing to it.

There is a unique sequence of directed edges from the root to each node.
这里写图片描述

For example, consider the illustrations below, in which nodes are represented by circles and edges are represented by lines with arrowheads. The first two of these are trees, but the last is not.

In this problem you will be given several descriptions of collections of nodes connected by directed edges. For each of these you are to determine if the collection satisfies the definition of a tree or not.

Input
The input will consist of a sequence of descriptions (test cases) followed by a pair of negative integers. Each test case will consist of a sequence of edge descriptions followed by a pair of zeroes Each edge description will consist of a pair of integers; the first integer identifies the node from which the edge begins, and the second integer identifies the node to which the edge is directed. Node numbers will always be greater than zero.
Output
For each test case display the line Case k is a tree." or the lineCase k is not a tree.”, where k corresponds to the test case number (they are sequentially numbered starting with 1).
Sample Input
6 8 5 3 5 2 6 4
5 6 0 0
8 1 7 3 6 2 8 9 7 5
7 4 7 8 7 6 0 0
3 8 6 8 6 4
5 3 5 6 5 2 0 0
-1 -1
Sample Output
Case 1 is a tree.
Case 2 is a tree.
Case 3 is not a tree.

AC代码:

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>#include<algorithm>#define MAX 100000+10using namespace std;struct node{    int num;//数据    int root;//根    int conn;//入度}p[MAX];void init(){    for(int i=1;i<=MAX;i++)    {        p[i].conn = 0;        p[i].root= i;        p[i].num=0;    }}int find(int x){    if(p[x].root==x)        return x;    return p[x].root=find(p[x].root);}void mix(int x,int y){    int fx=find(x);    int fy=find(y);    if(fx!=fy)    {        p[fy].root=fx;    }}int main(){    int n,m;    int i = 1;    bool flag=true;    init();    while(scanf("%d%d",&n,&m)!=EOF&&n>=0&&m>=0)    {        if(!flag && n!=0 && n!=0)            continue;        if(n == 0 && m == 0)        {            int root_num=0;            for(int j = 1; j < MAX; j++)            {                if(p[j].num && find(j)==j)                {                    root_num++;                }                       if(p[j].conn > 1)                {                    flag = false;                    break;                }            }            if(root_num>1)                flag=false;            if(flag)            printf("Case %d is a tree.\n",i++);            else                 printf("Case %d is not a tree.\n",i++);            flag = true;            init();            continue;        }        if(m!=n && find(n) == find(m))        flag = false;        else        {            p[m].num = 1;            p[n].num = 1;            p[m].conn++;            mix(n,m);        }    }    return 0;}

这道题感觉好难。博主词语表达能力不太好,给大家推荐一篇博文,讲的很详细。附上超链接。Is it aTree?

原创粉丝点击