前一段时间学了简单的并查集,网上逛技术博客,看到并查集的拓展,在原有并查集的基础上,加入集合内部元素和其父节点之间的关系,这样的拓展,可以解决更多问题
题目链接:
http://poj.org/problem?id=2492
题目大意:
输入n个bug,bug之间有interaction,当前假设异性之间才interaction,但是需要验证,给定这些interaction对,判定是否满足假设
如果男<->女和女<->男,满足条件,如果存在男<->男或者女<->女,则假设不满足
方法:
采用并查集拓展,加入relations[]数组,其中relations[x]表示x和父节点关系,relations[x] = 0表示x和父节点关系为同性,relations[x] = 1表示x和父节点关系为异性
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
#define MAX 5005
int parent[MAX]; //记录父节点
int relations[MAX]; //relations[x]记录x和父节点之间的关系
//其中relations[x] = 0表示x和父节点关系为同性, relations[x] = 1表示x和父节点关系为异性
//集合查找
int findSet(int x)
{
int temp = parent[x];
if(temp == x)
return x;
parent[x] = findSet(temp);
relations[x] = (relations[x] + relations[temp]) % 2; //根据老的父节点和新父节点关系,修改relations[x]值
return parent[x];
}
//合并集合
void unionSet(int root1, int root2)
{
int x = findSet(root1);
int y = findSet(root2);
if(x == y)
return ;
parent[x] = y;
relations[x] = ((relations[root1] - relations[root2] + 1) % 2);
return ;
}
//初始化数据
void init(void)
{
memset(relations, 0, sizeof(relations));
for(int i = 0; i < MAX; i++)
parent[i] = i;
return ;
}
int main()
{
int temp1, temp2;
int n, m;
int num;
int count = 1;
scanf("%d", &num);
while(num--)
{
scanf("%d%d", &n, &m);
init();
int flag = 1;
for(int i = 0; i < m; i++)
{
scanf("%d%d", &temp1, &temp2);
if(findSet(temp1) == findSet(temp2))
{
if(relations[temp1] != (relations[temp2] + 1) % 2) //如果不满足异性关系,有矛盾
flag = 0;
}
else
{
unionSet(temp1, temp2);
}
}
if(flag)
{
printf("Scenario #%d:\nNo suspicious bugs found!\n\n", count++);
}
else
{
printf("Scenario #%d:\nSuspicious bugs found!\n\n", count++);
}
}
return 0;
}