POJ1182食物链(理解种类并查集)

关键词:并查集
(不提供题目和源码,因为懒)
=============================================
查阅了很多关于此题的资料,大概有两种解法:
方法一,带权并查集。令x与y是同类=0,x吃y=1,x被y吃=2,将逻辑关系转化为权值计算。
方法二,种类并查集。开一个N3大小的数组,用x、x+N、x+2N来表示不同的种类。
第一种方法已经有人很好地解释了,就不搬砖了,主要来看第二种方法。
仔细阅读方法二的源码(网上的优质源码来源于《挑战程序设计竞赛》p89),会发现在判断假话时遇到逻辑上的疑惑:

#x和y属于同一类时
if (same(x, y+N) || same(x, y+2*N)){#若x和y不属于同一类,则假话数增1
    ans++;
}
......
#x吃y时
if(same(x, y) || same(x, y+2*N)){#若x不吃y,则假话数增1
    ans++;
}

Q:x、x+N、x+2*N到底代表着什么类?

如果x、x+N、x+2*N分别表示x∈A、x∈B、x∈C,那么在判断x和y不是同类时,应该要满足以下条件:
x∈A&&y∈B || x∈A&&y∈C || x∈B&&y∈A || x∈B&&y∈C || x∈C&&y∈A || x∈C&&y∈B
可是为什么源码只判断了x∈A&&y∈B || x∈A&&y∈C 呢?说明这种解释是有问题的(或者有其他大佬能帮忙解释一下,小弟不胜感激)。
经过好几天的沉思,我得出了一种比较合理的解释:x代表与x的同类,x+N代表x的天敌类,x+2*N代表x的食物类(有点奇怪为什么不是x+N是食物类呢?其实也可以,但源码是按这个逻辑来的,读者完全可以自己规定)。从而可以完整地解释上述代码逻辑:


same(x, y+N) || same(x, y+2*N):如果x和y的天敌类是同类或者x和y的食物类是同类,那么假话数增1,显然这就不用关心x和y到底是哪种具体的动物了。
same(x, y) || same(x, y+2*N):如果x和y是同类或者x和y的食物类是同类,那么假话数增1。


在合并时候的逻辑也可以理解了:

#如果x和y是同类,那么
unite(x, y);              #合并x的同类和y的同类为同类
unite(x+N, y+N);          #合并x的天敌类和y的天敌类为同类
unite(x+2*N, y+2*N);      #合并x的食物类和y的食物类为同类
......
#如果x吃y,那么
unite(x, y+N);            #合并x的同类和y的天敌类为同类
unite(x+N, y+2*N);        #合并x的天敌类和y的食物类为同类
unite(x+2*N, y);          #合并x的食物类和y的同类为同类

与此类似的有一道五行“金木水火土”相克的问题,其实就是加长版的食物链,这时候对于x而言就有4个不同种类:x的同类,x的下一个类(被x克制的类),x的下二个类,x的下三个类,x的下四个类(克制x的类)。

你可能感兴趣的:(POJ1182食物链(理解种类并查集))