Weighted disjoint-set (hdoj 3038, poj 1182, poj 2492)

Wei learned weighted three-day disjoint-set emmm, the basic content is actually quite mastered
with the right and the most important thing is to understand set of offset, and the offset update method of investigation, where the main vector of the application of knowledge . With examples to illustrate it.

hdoj 3038 How Many Answers Are Wrong

Topic effect: TT FF and do a game (boring). Providing a number of indices i and j numbers, i.e., to a [i], a [i + 1], a [i + 2] ..., a [j] , and determines whether a given correct answer, if it is determined not just as a correct answer.

Solution: This problem with the right to use disjoint-set, that is, each element to the root of the distance, that is the offset. Expressed dis. Initialized to 0. Shift amount calculated by the vector more convenient.

When the root node is not the same two points, it is necessary to merge two nodes, a root node is disposed at this time RA, the root node b c rb, a distance b is input to the analysis, because this when not judgment obtained and properly, so the default is the correct answer. Merge two nodes, the root node as the calculation method rb ra, the DIS [ra] As shown in (a Vector)
Here Insert Picture Description
when two root nodes are the same, then it can be determined given if the value is a correct answer, since when looking at the root node has been hung on each root, forms are shown below, and thus between the two points is calculated given, only dis [a] -dis [b] to, as shown in particular (vector).
Here Insert Picture Description
Here, the offset is over, take a look at how to find the root node.

Before, I look for the root node are written like emmm

int find_root(int x)
{
    while(parent[x]!=x)
        x=parent[x];
    return x;
}

这么写只是从上到下查找根节点,并不会路径压缩,即就算6->7->8,查找到6的根节点是8,并不会把6直接挂到8上面,即不会进行路径压缩。
然后看了dalao的代码

int find_root(int x)
{
    return parent[x]==x ? x : parent[x]=find_root(parent[x]);
}

WTF!!!(对8起,我爆粗口了)
这段代码也就是这样的:

int find_root(int x)
{
    if(parent[x]==x)
        return x;
    else
        return parent[x]=find_root(parent[x]);
}

在查找根节点的时候已经将它挂在根节点上了,也就是如图的亚子~
Here Insert Picture Description
Soga~~
从图中也能看出来,在每次递归的时候都需要更新dis的值,即加上其父节点的dis值。。
因此find_root函数就可以这么表示:

int find_root(int x)
{
    if(parent[x]!=x)
    {
        int tmp=parent[x];
        parent[x]=find_root(parent[x]);//递归查找根节点
        dis[x]+=dis[tmp];//更新dis
    }
    return parent[x];
}

最后贴上AC代码:

#include <iostream>
using namespace std;
int n,m;
int parent[200010],dis[200010];
void init()
{
    for(int i=0;i<=n;i++)
    {
        parent[i]=i;
        dis[i]=0;
    }
}
int find_root(int x)
{
    if(parent[x]!=x)
    {
        int tmp=parent[x];
        parent[x]=find_root(parent[x]);//递归查找根节点
        dis[x]+=dis[tmp];//更新dis
    }
    return parent[x];
}
int main()
{
    while(cin>>n>>m)
    {
        init();
        int ans=0;
        for(int i=0;i<m;i++)
        {
            int a,b,c;
            cin>>a>>b>>c;
            a--;
            int aa=find_root(a);
            int bb=find_root(b);
            //判断两点的根节点是否相同,如果相同则
            //说明两点都与根节点有关,可以直接判断是否是错误答案
            //如果根节点不相同,则说明无法从已知条件判断是否是错误答案
            //也就是没必要判断,将两点合并即可
            if(aa==bb)
            {
                if(dis[a]-dis[b]!=c)
                    ans++;
            }
            else
            {
                parent[aa]=bb;
                dis[aa]=-dis[a]+c+dis[b];//向量计算
            }
        }
        cout<<ans<<endl;
    }
}

poj 1182 食物链
题目大意: 动物王国有一种食物链(反正我觉得很神奇),A吃B,B吃C,C吃A(环形),要求你判断给出的关系是否正确,如果判断不了,则默认是正确的。
题解: 此题是并查集必做的题,和上一题差不多,重点也是在偏移量这里。参见dalao博客
当第二三中情况都排除的时候,来判断第一种情况。

首先将偏移量设为三种情况:
dis=0 表示x和父节点是同类
dis=1 表示x被父节点吃
dis=2 表示x吃父节点

在寻找根节点时,因为在找到之后就会将其直接挂到根节点上,因此dis[x]也要更新(在回溯时)。假设x,y的关系为r1,y,z的关系为r2,则x,z的关系为(r1+r2)%3 (具体原因请参见大佬博客)

当x和y的父节点相同时:(此时可以判断给出的关系是否正确)
若d=1,即x和y是同类,只要判断dis[x]和dis[y]是否相等即可。
若d=2,即x吃y,在所有表示“吃”的关系中,都遵循着一个规律:(dis[x]+1)%3=dis[y] ,若x,y满足这个关系,则说明是正确的。

当x和y的父节点不相同时:(此时无法判断,直接合并)
将x的根节点作为y的根节点的根节点
若d=1,即x和y是同类,则y对x的关系为0
若d=2,即x吃y,则y对x的关系为1
综上:无论d是1还是2,y对x的关系都为d-1
则dis[ry]的求法就如下图所示了(向量)
Here Insert Picture Description
这个ry->y是3-dis[y],得到的关系正好是与y->ry相反
贴上AC代码:

#include <iostream>
#include <stdio.h>
using namespace std;
int n,k;
int parent[50001],dis[50001];
//dis=0 x与父节点同类
//dis=1 x被父节点吃
//dis=2 x吃父节点
void init()
{
    for(int i=1;i<50001;i++)
    {
        parent[i]=i;
        dis[i]=0;
    }
}
int find_root(int x)
{
    if(parent[x]!=x)
    {
        int tmp=parent[x];
        parent[x]=find_root(parent[x]);
        dis[x]=(dis[x]+dis[tmp])%3;
    }
    return parent[x];
}
int main()
{
    cin>>n>>k;
    init();
    int ans=0;
    while(k--)
    {
        int x,y,d;
//        cin>>d>>x>>y;
        scanf("%d %d %d",&d,&x,&y);//这里必须用scanf,用cin WA了好几发
        if(x>n||y>n)//第二个判别条件
        {
            ans++;
            continue;
        }
        if(d==2&&x==y)//第三个判别条件
        {
            ans++;
            continue;
        }
        int xx=find_root(x);
        int yy=find_root(y);
        if(xx==yy)//如果根节点相同则进行判断
        {
            if(d==1)
            {
                if(dis[x]!=dis[y])
                    ans++;
            }
            else
            {
                if((dis[x]+1)%3!=dis[y])
                    ans++;
            }
        }
        else//如果根节点不同则合并
        {
            parent[yy]=xx;
            dis[yy]=(3-dis[y]+d-1+dis[x])%3;
        }
    }
    cout<<ans<<endl;
}

poj 2492 A Bug’s Life
题目大意: 一种生物有雌雄之分,并且正确的交配方法是雌雄交配,一位教授想判断他们交配方法是否正确(判断是否是同性恋。。)
题解: 此题要用到带权并查集(关于种类并查集和带权并查集的区别,请点击这里
首次,设一个dis数组表示与根节点的关系。当值为0时,表示同性,当值为1时,表示异性。首先初始化为0。
寻找父节点时,更新dis值:
Here Insert Picture Description
找到规律:dis[x]=(dis[x]+dis[rx])%2

当两个数据的根节点相同时,只要判断他们的dis值是否相同就行,如果dis值相同说明他们和根节点的关系相同,则他们是同性。反之相反。

When the root node of two data are different, need to be consolidated, calcd dis [rb] When combined, the vector can be used, may find the law. As shown:
Here Insert Picture Description
Since the opposite direction of the vector does not affect the vector value (two bug relationship from any bug see are the same), is obtained:
DIS [RB] = (DIS [A] + DIS [B] + 1'd) 2%
the AC codes:

#include <iostream>
#include <cstdio>
using namespace std;
int n,m,flag=0;
int parent[2001],dis[2001];
void init()
{
    for(int i=0;i<2001;i++)
    {
        parent[i]=i;
        dis[i]=0;
    }
}
int find_root(int x)
{
    if(parent[x]!=x)
    {
        int tmp=parent[x];
        parent[x]=find_root(parent[x]);
        dis[x]=(dis[x]+dis[tmp])%2;
    }
    return parent[x];
}
int main()
{
    int k;
    cin>>k;
    for(int i=0;i<k;i++)
    {
        scanf("%d %d",&n,&m);
        flag=0;
        init();
        while(m--)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            int aa=find_root(a);
            int bb=find_root(b);
            if(aa==bb)
            {
                if(dis[a]==dis[b])
                    flag=1;
            }
            else
            {
                dis[bb]=(dis[a]+dis[b]+1)%2;
                parent[bb]=aa;
            }
        }
        cout<<"Scenario #"<<i+1<<":"<<endl;
        if(flag)
            cout<<"Suspicious bugs found!"<<endl;
        else
            cout<<"No suspicious bugs found!"<<endl;
        cout<<endl;
    }
}
Published 32 original articles · won praise 12 · views 1388

Guess you like

Origin blog.csdn.net/qq_18873031/article/details/99443060