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 动 ...
随机推荐
- 【ZOJ 3480】Duck Typing
题 题意 1.有t组数据,输入时每组数据之间空格隔开,输出时也要求空格隔开. 2.每组都是一行begin开始,一行end结束. 3.class ClassName[:Super] 表示声明一个类型,S ...
- C# WPF 显示图片和视频显示 EmuguCv、AForge.Net测试(续)
介绍 本文是接着上文<C# WPF 显示图片和视频显示 EmuguCv.AForge.Net测试>写的,建议先看下上文,因为有些代码还需要了解. 增添 接着上文的代码,我们可以在事件处理方 ...
- QA要懂的Linux命令
<一>软件安装相关QA经常需要安装测试软件(jmeter.Mock.python环境搭建.java环境搭建),或者配置测试环境(nginx.ci等),需要了解linux下如何安装软件.在工 ...
- CentOS下yum安装VNCserver
VNC全称是Virtual Network Computing,属于远程控制类软件.其优点是支持跨操作系统的远程图形化控制.在日常工作中,服务器常常是存在机房,不可能每次需要图形界面操作就跑到机房,因 ...
- Spring学习6-Spring整合Struts2
一.Spring为什么要整合Struts2 Struts2与Spring进行整合的根本目的就是要让 Spring为Struts2的Action注入所需的资源对象,它们整合的原理则是只要导入了s ...
- html checkbox 全选与反选
之所以记录这个博客,是因为我完全用jquery,无法实现自己想要的结果(通过一个checkbox的选中或不选中控制其他多个checkbox状态) <input type="checkb ...
- Kd-tree算法原理
参考资料: Kd Tree算法原理 Kd-Tree,即K-dimensional tree,是一棵二叉树,树中存储的是一些K维数据.在一个K维数据集合上构建一棵Kd-Tree代表了对该K维数据集合构成 ...
- ubuntu安装wiz笔记
wiz笔记支持跨平台 下面记录一下如何在ubuntu下面安装wiz笔记 1,ubuntu默认是没有wiz资源的,需要先添加官方ppa软件仓库 sudo add-apt-repository ppa:w ...
- 【原创】express3.4.8源码解析之中间件
前言 注意:旧文章转成markdown格式. 中间件(middleware)的概念来自于TJ的connect库,express就是建立在connect之上. 就如同connect的意思是 连接 一样, ...
- kindle paperwhite折腾记
在亚马逊官网上买了一个kindle paperwhite 一代(849元) , 打算再买个皮套, 淘宝店 http://detail.tmall.com/item.htm?spm=a230r.1.1 ...