ACM模版——并查集

注释版: 

/* 普通版(无路径压缩) */
int pre[1000];
int find(int x)                // 查找我(x)的掌门
{
    int r=x;                   // 委托 r 去找掌门
    while(pre[r]!=r)           // 如果r的上级不是r自己(也就是说找到的大侠他不是掌门 = =)
    r=pre[r];                  // r 就接着找他的上级,直到找到掌门为止。
    return r;                  // 掌门驾到~
}
/* 升级版(有路径压缩) */
int pre[1000];
int find(int x)                     // 查找根节点
{ 
    int r=x;
    while( pre[r]!= r )             // 返回根节点 r
         r=pre[r];
 
    int i=x, j;
    while( i != r )                 // 路径压缩
    {
         j = pre[i]; 		    // 在改变上级之前用临时变量 j 记录下他的值 
         pre[i] = r ; 		    // 把上级改为根节点
         i = j;
    }
    return r ;
}
void join(int x,int y)             // 我想让虚竹和周芷若做朋友
{
    int fx=find(x),fy=find(y);     // 虚竹的老大是玄慈,芷若MM的老大是灭绝
    if(fx!=fy)                     // 玄慈和灭绝显然不是同一个人
        pre[fx]=fy;                    // 方丈只好委委屈屈地当了师太的手下啦,注意这里谁是谁的root,以及这里是 x 的祖先成为 fy 的子孙,而不是 x 成为 fy 的子孙
}

 

简化版:

int pre[1000];

int find(int x) // 普通最简版
{
    return pre[x]==x ? x : pre[x]=find(pre[x]);
}

int find(int x) // 优化版                    
{ 
    int r=x;
    while( pre[r]!= r )            
         r=pre[r];
 
    int i=x, j;
    while( i != r )                 
    {
         j = pre[i]; 		    
         pre[i] = r ; 		    
         i = j;
    }
    return r ;
}

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

 

应用:

/* 统计 root 结点个数(必要时适当修改) */
int cnt=0;
for(int i=1;i<=n;i++) 
    if(pre[i]==i) // 有局限性,前提确保在 n 的范围内,而且一定会出现[1,n]
        cnt++;


/* 统计每个 root 的内部总数(必要时适当修改) */
int rs=0;
for(int i=1;i<=n;i++)
{
    int rt=find(i);
    vis[rt]++;
    rs=max(rs,vis[rt]);
}

 

常见问题:

  1. 并查集中,不能参与统计 / 运算操作,因为会出现中途连接之前断开的结点,这样之前的计算就会有误差,所以要并查集完了之后,再做这类操作。

你可能感兴趣的:(#,ACM,#,并查集,#,ACM,模板)