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

来源:互联网 发布:淘宝新品标签规则 编辑:程序博客网 时间:2024/05/22 15:10

题目链接:

点击打开链接

题目大意:

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

题目分析:

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

我们定义两个数组,第一个数组就是实现并查集的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 <iostream>#include <cstring>#include <cstdio>#include <algorithm>#define MAX 50007using 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 );    }}



0 0
原创粉丝点击