先上题目:
动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。
A 吃 B,B 吃 C,C 吃 A。
现有 N 个动物,以 1∼N 编号。
每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这 N 个动物所构成的食物链关系进行描述:
第一种说法是 1 X Y
,表示 X 和 Y 是同类。
第二种说法是 2 X Y
,表示 X 吃 Y。
此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。
当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
- 当前的话与前面的某些真的话冲突,就是假话;
- 当前的话中 X 或 Y 比 N 大,就是假话;
- 当前的话表示 X 吃 X,就是假话。
你的任务是根据给定的 N 和 K 句话,输出假话的总数。
翻译一下假话的意思就是:
1.如果x,y是同类,但是x的捕食域有y,那么错误
2.如果x,y是同类,但是x的天敌域有y,那么错误
3.如果x是y的天敌,但是x的同类域中有y,那么错误
4.如果x是y的天敌,但是x的天敌域中有y,那么错误
并查集
对于本题,我们采用并查集维护每个节点到根节点的距离。
1.定义部分
int p[x],d[x]
p[x] 用来存储每个结点的编号,也就是下标;
d[x] 用来存储每个结点与自己父节点之间的距离。
2.初始化部分
for (int i = 1;i<=n;++i)
p[i] = i;
3.find()函数
int find(int x)
{
if (p[x] != x)
{
int t = find(p[x]);
d[x] = d[p[x]];
p[x] = t;
}
return p[x];
}
采用 递归 的形势找到每个结点的祖宗节点
递归思想:
分析递归过程,可以采用回溯的方法,即从递归能够终止的情况往回分析。
用图来表示
4.分析题目
因此对于判断错误的条件 1 和 2 :
1.如果x,y是同类,但是x的捕食域有y,那么错误
2.如果x,y是同类,但是x的天敌域有y,那么错误
用这样一段代码判断:
int px = find(x),py = find(y);//用px记录x结点的祖宗节点,py记录y结点的祖宗结点
if(px == py && (d[x]-d[y] % 3 !=0) )//如果px == py说明x和y在同一个集合之中,如果此时
//x结点和祖宗结点的距离与y结点和祖宗节点的距离的差值取模3
//不为0,说明,x和y结点不为同类
fake++;
如果x和y的祖宗节点不同,就要让他们合并为一个集合:
else if (px != py)
{
p[px] = py;
d[px] = d[y]-d[x];
}
用图来说明
对于条件 3,4
3.如果x是y的天敌,但是x的同类域中有y,那么错误
4.如果x是y的天敌,但是x的天敌域中有y,那么错误
用这样一段代码判断:
if(px == py && (d[x]-d[y]-1)%3 != 0)
fake++;
else if (px != py)
{
p[px] =py;
d[px] =d[x]-d[y]+1;
}
此时 x 吃 y ,那么此时 x 结点距离祖宗节点的距离与 y 结点距离祖宗节点的差值取模 3 应该为 1
,所以如果 x 能吃 y ,(d[x]-d[y]-1)%3 = 0,如果不等于 0 ,说明 x 结点不能吃 y 。
当 x 结点与 y 结点祖宗结点不一样时,一样要合并集合。