这两天做了几道并查集的题目,hdu的联通工程啊more is better 啊,然后卡在hdu1829,带权的并查集,没搞懂,尝试写下来让思路清晰些。
并查集是一种维护不同集合,在此基础上实现快速判断,统计个数等等的算法。
基础的有find和join两个功能,其中join作用于接收新数据。
并查集应用场景的几个特点:
1.数据之间存在联系;
2.借由两个数据的联系,数据会被双向联通的结合成几个集合;
下面介绍下find和join的基本形式
const int maxn=1000;int p[maxn];//用于储存数据的根void init(int n){ for(int i=0;i
这种是将关系优化避免出现树变成一条路
还有一种如下
int find(int x){ if(p[x]==x)return x; int tem=find(p[x]); return tem;}
这种用递归的方法算
join函数:
void join(int x,int y){ int f1=find(x),f2=find(y); if(f1!=f2) { p[f1]=f2; }}
知道了这些,一些水题就可以做了
接着是在这个基础上的变化,如统计集合个数,一开始的想法是维护完数据后for一遍,实际这里可以发现,在p[]数组中,作为根节点的数据p[i]==i的,没有必要通过链表结构特点去找根节点。接着处理掉这个for循环:
再看看现在的思路,是通过遍历一遍p[]数组,统计满足p[i]==i的个数,假设为a。如果所有数据没有联通,那么集合个数为n,a=n-(原本p[i]==i的点的p[i]改变的次数),即在join函数中每次寻找到两个数据的根数据进行合并时加一个计数器,然后用n减去。可以这样实现:
int ans=n;void join(int x,int y){ int f1=find(x),f2=find(y); if(f1!=f2) { p[f1]=f2; ans--; }}
接着是找集合中元素个数的题1856,这个可以通过增加一个初始化为1的数组,每次合并同时对应合并,同时max取最大值
#include #include #include #include #include #include #include #include #include #include #include
还有一个应用是判断是否成环,如果join的两个数据p[]相同,则标记为成环,如1272这题,还要判断唯一性,可以用前面的方法,也可以通过图论中边数为定点数减一来判断
#include #include #include #include #include #include #include #include #include #include #include
然后是1198这题,这题没有显示的给出数据间的关系,需要对数据进行处理,但并查集的部分没有什么新东西
#include #include #include #include #include #include #include #include #include #include #include
然后是现在做的这题,关于带权并查集,还没有弄明白,1829,题意大概是选择动物,给出两个数据的性别相反,问是否出现前后矛盾的情况,上网看到一种解法,思路是开两个数组,每次接收数据,交换p[]中两个数据对应的值,如果有处理到两个数据的p[]相同,则错误。还有一种是带权并查集。做了一个晚上带权那种,还是有点没搞明白。
带权并查集多一个权值的数组,通过递归的find函数找到根节点。现在的问题是,每次找到根节点的过程和合并的过程,会让原本a->b->c的关系变成a->c,b->c每次更新后都要再次调整权值数组。网上的解释是把权值看成向量。例如sign[i]表示由i指向前一个节点的向量,那么改变后的指向由a->c,等于sign[a]+sign[p[a]]。
下面是代码:
#include #include #include #include #include #include #include #include #include #include #include
现在还有个地方没有很清楚,就是在join函数中的sign[f1]的更新,为什么可以直接由sign[x]和sign[y]关系推到?
想通了!在find的过程中已经让x链接到根节点了
2017/4/26
今天学习了poj那道经典的食物链,有三个状态,看了结题报告,有两个细微不同的思路,一个是在状态数组sign里存0,1,2代表被吃,同类,吃的关系,另一种是在sign中存三种动物的种类,区别在更新sign数组的表达式上似乎有所不同。
通过这道题,一方面加深了关于向量关系的理解,另一方面是感觉数设的卡诺图化简作为二进制码逻辑关系的化简,这种思路在这种题里意外的有用【捂脸】
#include #include #include #include #include #include #include #include #include #include #include