POJ 1182 食物链 并查集
这个题算是考察一下大家的逻辑思维,很可惜,我的还不行,这题我搜了别人的解题报告,没看几行字就不想看了,但今天又很想A了这题,又一次去搜了解题报告,看到了CSDN的博客上有一篇名为史上POJ 1182 最详细的解题报告,写得不错,我终于耐心看了下去,看完后,发现这个题和那个称重的并查集的题的思路基本上是一样一样的啊。
下面详细讲一下:
先定义每个动物的两种属性,一个是父结点是谁,一个是和父结点的关系,定义如下:
struct Animal
{
int parent; //父结点的编号
int relation;//和父结点的关系,0-和父结点是同类 1-被父结点吃 2-吃父结点
} a[MAXN];
下面推两个式子:(爷爷是父亲的父结点,父亲是儿子的父结点,现在要路径压缩,令儿子直接指向爷爷,儿子和爷爷的relation值如何确定?看下表:)
p1 p2 p3(p1代表父亲的relation值,p2表示儿子的relation初值,p3代表儿子直接指向爷爷时,对爷爷的relation值)
0 0 0
0 1 1
0 2 2
1 0 1
1 1 2
1 2 0
2 0 2
2 1 0
2 2 1
很明显,像找规律一样,发现有如下关系:p3 = (p1+p2)%3 (公式1)
有了这个公式,就好办了,再推一个公式:
如果x是y的父结点,y的relation值为p1.
如果改成y是x的父结点,那么x的relation值为p2,p1 和p2又有什么关系呢,显然有p1 = (3-p2)%3;(公式2)
下面列举出所有情况,
x吃y,p1 = 1,p2 = 2
y吃x,p1=2,p2 = 1
x与y是同类,p1=0,p2=0,满足上面的式子。
当输入d,x,y时,x,y不在同一集合中,那么就合并x,y(是真话)
在同一集合,根据和两子结点和父结点的relation值推出两子结点的relation值,和它说的不一样,它说的就是假话。
我们不用启发式合并(把结点数少的合并到结点数多的上)
而是直接把y所在的集合合并到x上,先令r1 = find(x), r2 = find(y).
关键是要找到当r2以r1为根结点时r2的relation值。
利用上面的公式1和公式2就能很快推出来,自己试着推一下吧````
下面贴代码,没看明白的就看一下代码,兴许就明白了
哦,再解释一句,这里之所以要把y所在集合合并到x的跟结点上,是为了巧妙的利用d的值,将x和y是同类以及x吃y一次性给解决了。
拿只笔自己试试看
1 #include <cstdio> 2 #define MAXN 50005 3 struct node 4 { 5 int parent; 6 int relation; //表示和父结点的关系,0-同类,1-被吃,2-吃 7 } a[MAXN]; 8 int find(int x) 9 { 10 int s = x; 11 int cur =0; 12 int st[MAXN]; 13 while(a[s].parent > 0) 14 { 15 st[cur++] = a[s].relation; 16 s = a[s].parent; 17 } 18 for(int i=cur-1; i>0; --i) 19 st[i-1] = (st[i]+st[i-1])%3; 20 cur = 0; 21 while(s != x) 22 { 23 node tmp = a[x]; 24 a[x].parent = s; 25 a[x].relation = st[cur++]; 26 x = tmp.parent; 27 } 28 return s; 29 } 30 void Union(int x,int y,int d) 31 { 32 int r1= find(x); 33 int r2 = find(y); 34 a[r2].parent = r1; 35 a[r2].relation = (d+2+a[x].relation-a[y].relation)%3; 36 } 37 int main() 38 { 39 // freopen("in.cpp","r",stdin); 40 int n,k; 41 scanf("%d%d",&n,&k); 42 for(int i=1; i<=n; ++i)//初始化 43 { 44 a[i].parent = -1; 45 a[i].relation = 0; 46 } 47 int num=0; 48 while(k--) 49 { 50 int d,x,y; 51 scanf("%d%d%d",&d,&x,&y); 52 if(x > n || y>n || (d == 2 && x==y)) 53 { 54 num++; 55 continue; 56 } 57 int xp = find(x); 58 int yp = find(y); 59 if(xp != yp) 60 Union(x,y,d); 61 else 62 { 63 if(d == 1 && (a[x].relation+3-a[y].relation)%3 != 0)num++; 64 if(d == 2 && (a[x].relation+3-a[y].relation)%3 != 2) num++; 65 } 66 } 67 printf("%d\n",num); 68 return 0; 69 }