博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
POJ -- 1182 食物链
阅读量:6307 次
发布时间:2019-06-22

本文共 3032 字,大约阅读时间需要 10 分钟。

hot3.png

题目网址 :

这是一道利用并查集模拟的题目,也是并查集的裸题。

乍一看好像有点麻烦,因为会觉得动物们来回吃来吃去情况就会变得越来越复杂。

其实无论X和Y是同类还是捕食关系,归根结底都是集合合并的问题(捕食关系“错位”一下也是对等合并)。

考虑什么时候会发生冲突?冲突的情况只有一个,那就是两个有交集的食物链合并,并且合并的时候发生错位,也就是ABC不对等合并。另外的情况都是元素X所在的食物链和元素Y所在的食物链没有交集,这样合并后更新就好。

举个例子,就是你可以ABC和ABC合并, 也可以ABC和DEF合并,但是绝对不能ABC和BCA或者ACB合并。 原因是你现在获取了两个元素X和Y,他们俩要么处于同一个食物链中,要么处于不同食物链中,不同食物链可以直接合并,相同食物链只能对等合并(其实也就是不用合并),不能错位合并。

并查集是一种树结构,有一个特别典型的特性:只要不是根节点,就先获取根节点再做处理。

其实看过并查集的实现就会发现,如果一个节点不是根节点,那么跟这个节点有关的信息都是没意义的。因为并查集只保证根节点的信息具有时效性,其他非根节点的节点都只有一个作用,那就是找到根节点。

贴下代码,这个模拟的过程还是比较恶心人的。

#include 
#include
using namespace std;const int maxn = 50050;int p[maxn], rank[maxn], prev[maxn], next[maxn];void makeset(int x){ p[x] = x; rank[x] = 1;}int findset(int x){ int px = x, i; while(p[px] != px) px = p[px]; while(x != px) { i = p[x]; p[x] = px; x = i; } return px;}int unionset(int x, int y){ x = findset(x); y = findset(y); if(rank[x] > rank[y]) { p[y] = x; return x; } else { p[x] = y; if(rank[x] == rank[y]) rank[y]++; return y; }}int main(){ // freopen("in.txt", "r", stdin); int N, K; cin >> N >> K; for(int i = 1; i <= N; i++) makeset(i); int ans = 0; for(int i = 0; i < K; i++) { int D, X, Y; scanf("%d%d%d", &D, &X, &Y); if(X > N || Y > N) { ans++; continue; } if(D == 1) { int x = findset(X), y = findset(Y); if(x == y) continue; if(y == prev[x] || y == next[x]) ans++; else { int a = prev[x], b = x, c = next[x]; int d = prev[y], e = y, f = next[y]; int o, p, q; if(a == 0) o = d; else if(d == 0) o = a; else o = unionset(a, d); p = unionset(b, e); if(c == 0) q = f; else if(f == 0) q = c; else q = unionset(c, f); prev[p] = o; next[p] = q; prev[o] = q; next[o] = p; prev[q] = p; next[q] = o; } } else { int y = findset(Y), x = findset(X); if(x == y) ans++; else { int a = x, b = next[x], c = prev[x]; int d = prev[y], e = y, f = next[y]; if(e == c || e == a) ans++; else { int o, p, q; if(b == 0) p = e; else p = unionset(b, e); if(d == 0) o = a; else o = unionset(a, d); if(c == 0) q = f; else if(f == 0) q = c; else q = unionset(c, f); prev[p] = o; next[p] = q; prev[o] = q; next[o] = p; prev[q] = p; next[q] = o; } } } } cout << ans << endl; return 0;}

记得对每个元素保存捕食它的集合和它捕食的集合,集合当然都是根节点,并查集保证这一点。

转载于:https://my.oschina.net/u/1780798/blog/637344

你可能感兴趣的文章
VMware.Workstation Linux与windows实现文件夹共享
查看>>
ARM inlinehook小结
查看>>
wordpress admin https + nginx反向代理配置
查看>>
管理/var/spool/clientmqueue/下的大文件
查看>>
HTML学习笔记1—HTML基础
查看>>
mysql dba系统学习(20)mysql存储引擎MyISAM
查看>>
centos 5.5 64 php imagick 模块错误处理记录
查看>>
apache中文url日志分析--php十六进制字符串转换
查看>>
Ansible--playbook介绍
查看>>
浅谈代理
查看>>
php创建桌面快捷方式实现方法
查看>>
基于jquery实现的超酷动画源码
查看>>
fl包下的TransitionManager的使用
查看>>
Factorialize a Number
查看>>
[USB-Blaster] Error (209040): Can't access JTAG chain
查看>>
TreeSet的用法
查看>>
防HTTP慢速攻击的nginx安全配置
查看>>
深入理解PHP内核(十四)类的成员变量及方法
查看>>
Spring Boot2.0+中,自定义配置类扩展springMVC的功能
查看>>
参与博客编辑器改版,我的礼物 感谢51cto
查看>>