zoukankan      html  css  js  c++  java
  • 并查集

    所谓并查集,就是将一些有关系的东西合并起来,用于方便查找。
    在此我也不讲太多理论的东西(理论参看高级数据结构.pdf中并查集部分),还是讲讲实际应用吧。
    顾名思义,并查集就是并着查的集合。对他的操作有两种:
       查找操作:(查找某个元素所在的集合):
         查找操作
    要表示一个集合,就要加上一个标记,那些用什么来标记呢,就用他的第一个元素。

      合并操作:(将两个集合合并):
    将所要和并的两个元素所在集合的标记节点合并。

    接下来我们来看几道例题:
       家    族
    描述 Description
    若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

    输入格式 Input Format
    第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有亲戚关系。接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
    输出格式 Output Format
    P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。


    该题是标准并查集经典题目,只要找标记就行了。
    程序如下:
    读入数据,将节点的标记记为他自己:
    var

    a:array[0..5000]of integer;

       i,j,k,m,n,p:integer;

    begin   readln(n,m,p);   for i:=1 to n do   a[i]:=i; end.
    操作:
    function fa(x:integer):integer;

    begin  

        if a[x]<>x then

          begin a[x]:=fa(a[x]);fa:=a[x]; end
           else fa:=x; end;

            for i:=1 to m do   begin  readln(j,k);  if fa(j)<>fa(k) then a[fa(k)]:=j;   end;判断,写出结果:
              for i:=1 to p do   begin  readln(j,k);  if fa(j)=fa(k) then writeln('Yes') else writeln('No');   end;


    团    伙
    某城市里住着n个人,任何两个认识的人不是朋友就是敌人,而且满足:
    1.我朋友的朋友是我的朋友。
    2.我敌人的敌人是我的朋友。
    所有的 朋友组成一个团伙。告诉你关于这n个人的m条信息(即某两个人是朋友,或某两个人是敌人),请你计算出这个城市最多有多少个团伙。
    输入格式 Input Format
    第一行:两个整数n,m,(n<=5000,m<=5000,),分别表示有n个人,m个关系。
    以下m行:每行三个数Mi,Mj,Mk,1<=Mi,Mj<=N,表示Ai和Bi具有某种关系(Mk=0:朋友;Mk=1:敌人)。
    输出格式 Output Format
    一个数,团伙的人数。
    这题没像刚才那题那么简单。只是做合并,那么就区分不了敌人和朋友 了。
    那该怎么办呢?
    其实还是可以用并查集做的,只是要加一个权值罢了:

    读入数据,将节点的标记记为他自己,路径权值为0:
    var

    a:array[0..5000,1..2]of integer;

    b:array[0..5000]of boolean;

    i,m,n,m1,m2,m3:integer;

    begin readln(n,m);

    for i:=1 to n do

    begin
         a[i,1]:=i;a[i,2]:=0;
    end.
    操作:
    function fa(x:integer):integer;
       begin
        if a[x,1]=x then
        begin fax:=0;fa:=x;end
        else
         begin
          a[x,1]:=fa(a[x,1]);
          fa:=a[x,1];
          a[x,2]:=(fax+a[x,2])mod 2;
          fax:=a[x,2];
         end;
       end;
    for i:=1 to   m do
        begin
         read(m1,m2,mk);
         if fa(m1)=fa(m2) then
          begin
           if a[m1,2]<>a[m2,2]xor mk=1 then   begin
             write('error');exit;
            end;
          end
         else
          begin
            a[fa(m2),2]:=(mk+a[m2,2])mod2;
            a[fa(m2),1]:=m1;
          end;
        end;
    计算团队数:
    sum:=0;
       for i:=1 to n do
        if i=fa(i) then inc(sum);
    for I:=1 to n do
       if (a[I,2]=1)and(not(b[a[I,1]]) then begin
       b[a[I,1]]:=true;inc(sum);
    End;
    writeln(sum);

    口袋的天空
    背景 Background
    小杉坐在教室里,透过口袋一样的窗户看口袋一样的天空。有很多云飘在那里,看起来很漂亮,小杉想摘下那样美的几朵云,做成棉花糖。
    描述 Description
    给你云朵的个数N,再给你M个关系,表示哪些云朵可以连在一起。现在小杉要把所有云朵连成K个棉花糖,一个棉花糖最少要用掉一朵云,小杉想知道他怎么连,花费的代价最小。
    输入格式 Input Format
    每组测试数据的第一行有三个数N,M,K(1<=N<=1000,1<=M<=10000,1<=K<=10)接下来M个数每行三个数X,Y,L,表示X云和Y云可以通过L的代价连在一起。(1<=X,Y<=N,0<=L<10000)30%的数据N<=100,M<=1000
    输出格式 Output Format
    对每组数据输出一行,仅有一个整数,表示最小的代价。如果怎么连都连不出K个棉花糖,请输出'No Answer'。

    分     析
    如果k=1 那么就是求做小生成树。那样的话,应该怎么做。
    Prim?
    还是别的什么算法?如果你能想到Kruskal算法,那就成功了。
    下面我来介绍一下Kruskal算法:
    Kruskal
    算法思想:kruskal算法选择n- 1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。注意到所选取的边若产生环路则不可能形成一棵生成树。
    kruskal算法分e 步,其中e 是网络中边的数目。按耗费递增的顺序来考虑这e 条边,每次考虑一条边。当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。
    那么怎么判断成环呢?
    当然是并查集了!我们只要判断所加的边的两个节点是否在同一个集合中,那就OK了!

    然后我们再来想想“口袋的天空”,似乎有些眉目了。
    那道题是不是相当于算到还剩下K个树时就结束呢?
    Of course yes.

    先将边排序,放入go数组
    (两个节点为go[*,2]和go[*,3]),然后进行添边操作:
    function fa(x:longint):longint;
    begin
       if a[x]=x then fa:=x
       else
       begin
        a[x]:=fa(a[x]);
        fa:=a[x];
       end;
    end;
    sum:=n;
    while (sum>k)and(i<=m) do
       begin
        if fa(go[i,2])<>fa(go[i,3]) then
        begin
         dec(sum);
         inc(min,go[i,1]);
         a[fa(go[i,3])]:=go[i,2];
        end;
        inc(i);
       end;


    最勇敢的机器人
    描述 Description
    机器人们都想知道谁是最勇敢的,于是它们比赛搬运一些物品。它们到了一个仓库,里面有n个物品,每个物品都有一个价值Pi和重量Wi,但是有些物品放在一起会爆炸,并且爆炸具有传递性。(a和b会爆炸、b和c会爆炸则a和c会爆炸)机器人们可不想因此损失自己好不容易从Wind那里敲诈来的装备,于是它们想知道在能力范围内,它们最多可以拿多少价值的物品。你能帮助它们吗?
    输入格式 Input Format
    每组测试数据第1行为n,Wmax,k(0<=n,Wmax,k<=1000)接下来n行,为每个物品的Pi,Wi(0<=Pi<=1000,1<=Wi<=10,均为整数)再接下来k行,每行2个数字a,b表示a和b会发生爆炸

    输出格式 Output Format
    对每组数据输出1行为最大可能价值
    初看此题,是不是像01背包问题。如果没有爆炸条件(也就是说,不会爆炸),那么就是经典的01背包问题。
    那怎么把爆炸条件加进去呢?
    当然还是可以用并查集的!   

  • 相关阅读:
    SpringDataJpa实体类常用注解
    I2C子系统
    input子系统
    platform深入
    运用层是怎样找到驱动的+open中子设备识别
    misc设备
    文章标题
    uboot2015第一阶段---SPL
    git工具使用
    Andriod底层第五课------HAL硬件抽象层
  • 原文地址:https://www.cnblogs.com/yaoliang11/p/1434813.html
Copyright © 2011-2022 走看看