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一次性给解决了。

拿只笔自己试试看

View Code
 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 }

 

           

 

转载于:https://www.cnblogs.com/allh123/archive/2013/04/11/3015276.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值