DisJSet:食物链(POJ 1182)
这一题有两种思路,先介绍第一种:
题目是中文的,我就不翻译了,也很好理解,就是一个A-B-C-A的一个循环的食物链,给定一些条件,问你哪些条件是错的
这一题,是一道比较经典的查并集的题目,那么这一题的思想是什么呢,对于给定的条件,x和y属于什么集合其实并没有给出,而且x和y之间还有其他的捕食的关系
但是我们现在假设,如果x和y都是属于同一类的,那么如果x属于A,那么y也一定属于A,那么也就是说,x和y属于同一个集合总是同时成立的的,再假设,如果x可以吃y,那么如果x属于A,那么y一定属于B,x属于B,那么y一定属于C或x属于C,那么y一定属于A,也就是说,在捕食关系中,y属于x的下一个集合一定成立的(A-B-C-A)
那么我们就可以用查并集来很好的维护这个关系,我们给x和y赋予A,B,C三个属性,并且用查并集来维护这些属性,如果x和y都属于一个集合,那么我们就要分别合并x和y的三个属性,如果属于不是关系,那么我们就把y的与x的下一个关系的集合进行合并,这三个属性可以用k,k+N,k+2*N的形式表现,这样就可以非常快速的维护关系了(因为对于找根的操作而言,查并集是最快的)
PS:查并集我用的是按大小合并的方式,本来这种方式可以有效的降低搜索的时间,但是因为这一题比较特殊(集合比较多)所以递归时间并没有减少很多,另外大家合并的时候一定要检查是不是同一个集合,不然会出错,并且MLE
另外这题,真是尼玛,有BUG,只能交单次数据(也就是不能while(~scanf())),真是个脑残bug
#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef int Position; Position Find(Position);
void Union_Set(Position, Position);
int If_Same_Set(Position, Position); static int *Set = NULL; int main(void)
{
int N, case_sum, i;
int ans = , inform, x, y;
scanf("%d%d", &N, &case_sum);
Set = new int[ * (N + )];
for (i = ; i <= * N; i++) Set[i] = -;
for (i = ; i < case_sum; i++)
{
scanf("%d%d%d", &inform, &x, &y);
if (x > N || y > N)//如果在区域外,直接判断出错
{
ans++;
continue;
}
else if (inform == )
{
if (If_Same_Set(x, y + N) || If_Same_Set(x, y + * N))
//如果已经是捕食集合中,出错
ans++;
else//否则,则把xy的三个属性分别合并
{
Union_Set(x, y);
Union_Set(x + N, y + N);
Union_Set(x + * N, y + * N);
}
}
else if (inform == )
{
if (If_Same_Set(x, y) || If_Same_Set(x, y + * N))
//如果已经在非捕食集或已统一集合,出错
ans++;
else
{
Union_Set(x, y + N);
Union_Set(x + N, y + * N);
Union_Set(x + * N, y);
}
}
}
printf("%d\n", ans);
delete Set;
return ;
} Position Find(Position x)
{
//路径压缩
if (Set[x] < )
return x;
else
return Set[x] = Find(Set[x]);
} int If_Same_Set(Position x, Position y)
{
return Find(x) == Find(y);
} void Union_Set(Position x, Position y)
{
Position Rootx, Rooty;
Rootx = Find(x);
Rooty = Find(y); if (Rootx != Rooty)//按大小求并,千万要注意不能是同一个集合的合并,不然会MLE
{
if (Set[Rootx] < Set[Rooty])
{
Set[Rootx] += Set[Rooty];
Set[Rooty] = Rootx;
}
else
{
Set[Rooty] += Set[Rootx];
Set[Rootx] = Rooty;
}
}
}
第二种思路,只用到一个查并集和一个偏移量集:思路来源http://poj.org/showmessage?message_id=105601
我们先定义B与A之间的偏移关系:
存在偏移量集delta,如果|delta[B]-delta[A]|%3,则定义
=0 B与A是同类
=1 B是A的食物
=2 B是A的天敌
我们规定,每一次关系的确定,最后B与A都会集中到一个集合(广义集合),其中的关系在偏移量集中找
比如如果规定1吃2,2吃3,4吃2,则说明如果当所有元素的初始化绝对偏移量是0时,则2的绝对偏移量是1,3的绝对偏移量是2(delta[3]-delta[1]=2所以3是1的天敌,符合问题描述),同时4的绝对偏移量是0(绝对偏移量只取012),同样满足关系。但是现在的问题是,我们的集合最后都会搜索到根的,如果我们仅改变某个元素的一次偏移量,可能这个元素就会和根的关系就会发生变化,但是根与其包括的所有元素的关系都是先确定好了,所以我们只用把根的关系都进行偏移就可以,比如我们再定义5吃6,6吃7,8吃6,(偏移量:5-0,6-1,7-2,8-0)同时再定义1吃5,9吃8,如果我们假设5合并到1上,那么5对1的相对偏移量应该是1,而和5这个集合的元素的绝对偏移量应该全部都+1,才能保证相对偏移量不变,也就是可以看成,以5为起点,5这个集合的所有元素全部加上5的绝对偏移量1
比如处理8吃9的时候,因为5的绝对偏移量为1,所以9的偏移量为delta[9]-delta[8]+5的绝对偏移量+1=0-0+1+1=2,刚好说明9是8的食物(此时8的绝对偏移量为0+1),同时也是5的食物,而且还是1的天敌,符合题意
我们最后可以用(delta[x]-delta[y]+d)%3=delta[rootx]的方法来添加绝对偏移量,对于根的绝对偏移量发生变化时,我们可以不必立马对根以下的元素全部进行改变,也改变不了,我们只要查询的时候进行操作再重新记录就可以了
#include <iostream>
#include <functional> using namespace std; typedef int Position; Position *pos = NULL;//查并集
Position *delta = NULL;//偏移量集 int Find(Position);
bool Check(Position, Position, const int); int main(void)
{
int N, case_sum, ans = , inform, x, y;
scanf("%d%d", &N, &case_sum);
pos = new Position[N + ];
delta = new Position[N + ];
for (int i = ; i <= N; i++)
pos[i] = i;
memset(delta, , sizeof(Position)*(N + )); for (int i = ; i < case_sum; i++)
{
scanf("%d%d%d", &inform, &x, &y);
if (x > N || y > N)
ans++;
else if (!Check(x, y, inform - ))
ans++;
}
printf("%d\n", ans);
delete pos; delete delta;
return ;
} int Find(Position x)
{
//路径压缩
if (pos[x] == x)
return x;
int tmp = Find(pos[x]); delta[x] = (delta[x] + delta[pos[x]]) % ;
pos[x] = tmp;
return tmp;
} bool Check(Position x, Position y, const int inform)
{
//设delta[x]为x到根的偏移量
Position Rootx, Rooty;
Rootx = Find(x);
Rooty = Find(y); if (Rootx == Rooty)
{
if ((delta[y] - delta[x] + ) % != inform)
return false;//如果在同一个集合,不满足偏移,说明出错了
else return true;//满足偏移量,则说明正确
}
//直接合并
pos[Rooty] = Rootx;
delta[Rooty] = (delta[x] - delta[y] + inform + ) % ;
return true;
}
DisJSet:食物链(POJ 1182)的更多相关文章
- 食物链 poj 1182
C - 食物链 Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%I64d & %I64u Submit Stat ...
- 洛谷 P2024 食物链 POJ 1182 Label:并查集Turbo
题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我 ...
- 食物链 POJ 1182(种类并查集)
Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到 ...
- Day5 - F - 食物链 POJ - 1182
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A.现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种.有人用两种说法 ...
- 食物链 POJ - 1182 (并查集的两种写法)
这是一个非常经典的带权并查集,有两种写法. 1 边权并查集 规定一下,当x和y这条边的权值为0时,表示x和y是同类,当为1时,表示x吃y,当为2时,表示x被y吃. 一共有三种状态,如图,当A吃B,B吃 ...
- poj 1182:食物链(种类并查集,食物链问题)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 44168 Accepted: 12878 Description ...
- POJ 1182 食物链
G - 食物链 Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%I64d & %I64u Submit Stat ...
- POJ 1182 食物链(种类并查集)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 63592 Accepted: 18670 Description ...
- poj 1182 食物链 (带关系的并查集)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 44835 Accepted: 13069 Description 动 ...
随机推荐
- spring获取ApplicationContext对象的方法——ApplicationContextAware
一. 引言 工作之余,在看一下当年学的spring时,感觉我们以前都是通过get~ set~方法去取spring的Ioc取bean,今天就想能不能换种模型呢?因为我们在整合s2sh时,也许有那么一天就 ...
- 【bzoj3289】 Mato的文件管理
http://www.lydsy.com/JudgeOnline/problem.php?id=3289 (题目链接) 题意 求区间逆序对 Solution 离线无修改查询,莫队转移:树状数组维护区间 ...
- float,double,decimal使用讨论
注意:有效位:小数点前后的全部数字,不包括小数点在内 float:浮点型,含字节数为4,32bit,数值范围为-3.4E38~3.4E38(7个有效位) double:双精度实型,含字节数为8,64b ...
- MVC模式介绍
MVC是一种通过3个不同部分构造一个软件或组件的理想办法: 1.模型(Model):用于存储数据的对象. 2.视图(View):为模型提供数据显示的对象. 控制器(Controller):负责具体的业 ...
- POJ2586Y2K Accounting Bug(贪心 + 不好想)
Y2K Accounting Bug Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 12251 Accepted: 62 ...
- KxMenu下拉菜单
+ (void)createMenu:(id)sender target:(UIViewController *)t { NSArray *menuItems = @[ [KxMenuItem men ...
- C++对象模型:单继承,多继承,虚继承
什么是对象模型 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分.对于各种支持的底层实现机制. 类中成员分类 数据成员分为静态和非静态,成员函数有静态非静态以及虚函数 clas ...
- DedeCms 5.x 本地文件包含漏洞(respond方法)
漏洞版本: DedeCms 5.x 漏洞描述: DedeCms是免费的PHP网站内容管理系统. plus/carbuyaction.php里没有对变量进行严格的过滤 出现漏洞的两个文件为: Inclu ...
- python多线程备份MYSQL数据库并删除旧的备份。
#!/usr/bin/python # -*- coding=utf-8 -*- import time import os import datetime import threading from ...
- [Socket网络编程]一个封锁操作被对 WSACancelBlockingCall 的调用中断。
原文地址:http://www.cnblogs.com/xiwang/archive/2012/10/25/2740114.html记录在此,方便查阅. C#中在使用UDPClient循环监听端口,在 ...