poj 1182 食物链(带权并查集)

题目链接:

点击打开链接

题目大意:

给出一些关系,判断矛盾的个数,先说出的未被反驳的语句我们认为是正确的

题目分析:

这种题很明显是集合的问题,但是是一个有关系的集合,所以我们可以利用带权的并查集来解决,主要做法如下:

我们定义两个数组,第一个数组就是实现并查集的fa数组,用来判断集合关系,把每个集合看做一条链,可以得到这条链的一端,我们定义它为根,因为关系满足传递性,所以我们可以通过关系的传递性推倒出当前条件下两者的关系,然后根据当前的可判断的关系来判断刚刚给出的关系是否出现矛盾

那么具体做法就是:

1.rank数组0表示当前点与根同类,1表示当前点能吃根,2表示当前点被根吃,如此定义下正好能够通过模运算,达到关系的传递性

2.首先用rank数组标记当前点与根的关系,然后我们可以利用传递性,得到任意两点间的关系((rank[a]-rank[b]) %3)

3.可以再路径压缩中维护这种关系,rank[a] = (rank[a]+rank[fa[a]] )%3, 通过将当前点到之前根的关系,加上之前根到当前根的关系,维护当前点到根的关系

4.每次添加新关系导致两个集合(两条链)连接,我们可以通过当前点的关系,反推出两个根的关系,也就是rank[ffa] = rank[a]-rank[b]+f

具体代码如下,这是带权并查集的经典题同样也是模板题,把并查集理解为一条链更容易理解

代码如下:

#include 
#include 
#include 
#include 
#define MAX 50007

using namespace std;

int rank[MAX];
int fa[MAX];
int ans;

int _find ( int x )
{
    //return x == fa[x]?x:fa[x]=find(x);
    if ( x == fa[x] ) return x;
    int temp = fa[x];
    fa[x] = _find ( fa[x]);
    rank[x] = (rank[x] + rank[temp])%3;
    return fa[x];
}

void init ( )
{
    for( int i = 0 ; i < MAX ; i++ )
        fa[i] = i;
}

void _union ( int a , int b , int f )
{
    int ffa = _find ( a );
    int fb = _find ( b );
    if ( ffa == fb ) return;
    fa[ffa] = fb;
    rank[ffa] = (f+rank[b]-rank[a]+3)%3;
}

int n,k,u,v,f;

bool check ( int u , int v , int f )
{
    if ( u > n || v > n ) return false;
    if ( f == 1 && u == v ) return false;
    if ( _find(u) == _find(v))
        return ((rank[u] - rank[v])%3+3)%3 == f;
    else return true;
}

int main ( )
{
    scanf ( "%d%d" , &n , &k );
    {
        ans = 0;
        memset ( rank , 0 , sizeof ( rank ));
        init ();
        for ( int i = 0 ; i < k ; i++ )
        {
            scanf ( "%d%d%d" , &f , &u , &v );
            f--;
            if ( check ( u , v ,f ))
                _union ( u , v , f );
            else ans++;
        }
        printf ( "%d\n" , ans );
    }
}



你可能感兴趣的:(C++,带权并查集,数据结构)