算法与数据结构-并查集

基本内容

并查集的存储

int fa[SIZE];

并查集的初始化

for (int i = 0; i < n; i++) {
  fa[i] = i;
}

并查集的Get操作

int get(int x){
	if(x==fa[x])	return x;
    return fa[x]=get(fa[x]);//路径压缩
}

并查集的Merge操作

void merge(int x,int y){
	fa[get(x)]=get(y);
} // 

关键词: “扩展域”与“边带权”

X-Plosives

X-Plosives - UVA 1160 - Virtual Judge (vjudge.net)

思路:我们把每个元素看成顶点,则一个简单化合物就是一条边。当整个图存在环的时候,组成环的边对应的化合物是危险的,反之则是安全的。
这样,我们可以用一个并查集来维护图的连通分量集合,每次得到一个简单化合物(x,y)时检查x和y是否在同一个集合中。如果是,则拒绝,反之则接受。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
import java.util.StringTokenizer;

public class Main {

  public static void main(String[] args) throws IOException {
    plosives();
  }

  static int[] parent = new int[100005];

  public static void plosives() {
    Scanner scanner = new Scanner(System.in);
    while (scanner.hasNext()){
      for (int i = 0; i < 100005; i++) {
        parent[i] = i;
      }
      int ans = 0;
      while (true) {
        int a = scanner.nextInt();
        if (a == -1) {
          break;
        }
        int b = scanner.nextInt();
        a = findSet(a);
        b = findSet(b);
        if (a == b) {
          ans++;
        } else {
          parent[a]=parent[b];  //省事 普通合并下
        }
      }
      System.out.println(ans);
    }
  }

  //带路径压缩
  public static int findSet(int x) {
    return parent[x] == x ? x : (parent[x] = findSet(parent[x]));
  }

}

Corporative Network

CCO Preparation Test 5 P2 - Corporative Network - DMOJ ccoprep5p2 - Virtual Judge (vjudge.net)

思路:因为题目只查询结点到根结点的距离,所以每棵树除了根结点不能换之外,其他结点的位置可以任意改变,这恰好符合并查集的特点,但是需要记录附加信息。如果记录每个结点到根的距离,那么每次Ⅰ操作都要更新很多结点的信息,时间复杂度难以保证,因此考虑记下每个结点到父结点的距离为d[i],然后在路径压缩时维护这个d数组。

package Uva;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
import java.util.StringTokenizer;

public class Main {

  /**
   * 快速输入类
   */
  static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
  static StringTokenizer tokenizer = new StringTokenizer("");

  /**
   * 获取下一段文本
   */
  static String next() throws IOException {
    while (!tokenizer.hasMoreTokens()) {
      tokenizer = new StringTokenizer(reader.readLine());
    }
    return tokenizer.nextToken();
  }

  static int nextInt() throws IOException {
    return Integer.parseInt(next());
  }

  static double nextDouble() throws IOException {
    return Double.parseDouble(next());
  }

  public static void main(String[] args) throws IOException {
    corporative();
  }

  static int[] pa = new int[20001];
  static int[] d = new int[20001];

  //带路径压缩,同时维护d[x]
  public static int findSet2(int x) {
    if (pa[x] != x) {
      int root = findSet2(pa[x]);
      d[x] += d[pa[x]];
      return pa[x] = root;
    }
    return x;
  }

  public static void corporative() throws IOException {
    int T = nextInt();
    for (int k = 0; k < T; k++) {
      int n = nextInt();
      for (int i = 0; i < n; i++) {
        pa[i] = i;
        d[i] = 0;
      }
      while (true) {
        String next = next();
        char c = next.charAt(0);
        if (c == 'O') {
          break;
        } else if (c == 'E') {
          int u =nextInt();
          findSet2(u);
          System.out.println(d[u]);
        } else {
          int u = nextInt();
          int v = nextInt();
          pa[u] = v;
          d[u] = Math.abs(u - v) % 1000;
        }
      }
    }
  }

}

P1955 [NOI2015] 程序自动分析

[P1955 NOI2015] 程序自动分析 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

数据量大可考虑离散化,下面使用哈希表。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.StringTokenizer;

public class Main {

  /**
   * 快速输入类
   */
  static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
  static StringTokenizer tokenizer = new StringTokenizer("");

  /**
   * 获取下一段文本
   */
  static String next() throws IOException {
    while (!tokenizer.hasMoreTokens()) {
      tokenizer = new StringTokenizer(reader.readLine());
    }
    return tokenizer.nextToken();
  }

  static int nextInt() throws IOException {
    return Integer.parseInt(next());
  }

  static double nextDouble() throws IOException {
    return Double.parseDouble(next());
  }

  public static void main(String[] args) throws IOException {
    NOI2015();
  }

  public static void NOI2015() throws IOException {
    int n = nextInt();
    Node[] nodes = new Node[1000000];
    HashMap<Integer, Integer> map;
    HashSet<Integer> set;
    for (int i = 0; i < n; i++) {
      int num = nextInt();
      set = new HashSet<>();
      map = new HashMap<>();
      for (int j = 0; j < num; j++) {
        nodes[j] = new Node(nextInt(), nextInt(), nextInt());
        set.add(nodes[j].x);
        set.add(nodes[j].y);
      }
      int[] arr = new int[set.size()];
      Iterator<Integer> iterator = set.iterator();
      int cur = 0;
      while (iterator.hasNext()) {
        arr[cur++] = iterator.next();
      }
      Arrays.sort(arr);
      for (int i1 = 0; i1 < arr.length; i1++) {
        map.put(arr[i1], i1);
        fa[i1] = i1;
      }
      for (int j = 0; j < num; j++) {
        if (nodes[j].e == 1) {
          fa[findSet3(map.get(nodes[j].x))] = findSet3(map.get(nodes[j].y));
        }
      }
      int flag = 0;
      for (int j = 0; j < num; j++) {
        if (nodes[j].e == 0 && findSet3(map.get(nodes[j].x)) == findSet3(map.get(nodes[j].y))) {
          System.out.println("NO");
          flag = 1;
          break;
        }
      }
      if (flag == 0) {
        System.out.println("YES");
      }
    }
  }

  static int[] fa = new int[1000000];

  public static class Node {

    int x;
    int y;
    int e;

    public Node(int x, int y, int e) {
      this.x = x;
      this.y = y;
      this.e = e;
    }
  }

  public static int findSet3(int x) {
    return fa[x] == x ? x : (fa[x] = findSet3(fa[x]));
  }
}

Supermarket

Supermarket - UVA 1316 - Virtual Judge (vjudge.net)

一种显而易见的贪心策略则是,优先考虑买出利润大的商品,并且对每个商品,在它过期之前尽量晚卖出——占用较晚的时间,显然对其他商品具有“决策包容性”。
于是我们可以把商品按照利润从大到小排序,并建立一个关于“天数”的并查集。起初每一天各自构成一个集合。对于每个商品,若它在d天之后过期,就在并查集中查询 d 的树根(记为r)。若r大于0,则把该商品安排在第r天卖出,合并r与r-1(令r为r-1的子节点),答案累加该商品的利润。
这个并查集实际上维护了一个数组中“位置”的占用情况。每个“位置”所在集合的代表就是从它开始往前数第一个空闲的位置(包括它本身)。当一个“位置”被占用时(某一天安排了商品),就把该“位置”在并查集中指向它前一个“位置”。利用并查集的路径压缩,就可以快速找到最晚能卖出的时间(从过期时间往前数第一个空闲的天数)。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;

public class Main {

  static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
  static StreamTokenizer in = new StreamTokenizer(reader);

  static String next() throws IOException {
    in.nextToken();
    return in.sval;
  }

  /**
   * 获取数字
   */
  static int nextInt() throws IOException {
    in.nextToken();
    return (int) in.nval;
  }

  static double nextDouble() throws IOException {
    in.nextToken();
    return in.nval;
  }

  public static void main(String[] args) throws IOException {
    superMarket();
  }

  static int[] fa = new int[10001];

  public static int findSet3(int x) {
    return fa[x] == x ? x : (fa[x] = findSet3(fa[x]));
  }

  public static void superMarket() throws IOException {
    int[][] arr = new int[10001][2];
    while (in.nextToken() != StreamTokenizer.TT_EOF) {
      int n = (int) in.nval;
      for (int i = 0; i < n; i++) {
        arr[i][0] = nextInt();
        arr[i][1] = nextInt();
      }
      Arrays.sort(arr, 0, n , (o1, o2) -> {
        return o2[0] - o1[0];
      });
      for (int i = 1; i <= 10000; i++) {
        fa[i] = i;
      }
      int ans = 0;
      for (int i = 0; i < n; i++) {
        int root = findSet3(arr[i][1]);
        if (root > 0) {
          fa[root] = root - 1;
          ans += arr[i][0];
        }
      }
      System.out.println(ans);
    }
  }
}

P1196 [NOI2002] 银河英雄传说

[P1196 NOI2002] 银河英雄传说 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

public class Main {

  static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
  static StreamTokenizer in = new StreamTokenizer(reader);

  static String next() throws IOException {
    in.nextToken();
    return in.sval;
  }

  /**
   * 获取数字
   */
  static int nextInt() throws IOException {
    in.nextToken();
    return (int) in.nval;
  }

  static double nextDouble() throws IOException {
    in.nextToken();
    return in.nval;
  }

  public static void main(String[] args) throws IOException {
    hero();
  }

  static int[] fa = new int[30001];
  static int[] w = new int[30001];
  static int[] size = new int[30001];

  public static int findSet(int x) {
    if (x == fa[x]) {
      return x;
    }
    int root = findSet(fa[x]);
    w[x] += w[fa[x]];
    return fa[x] = root;
  }

  public static void merge(int x, int y) {
    int root = findSet(y);
    int a = findSet(x);
    fa[a] = root;
    w[a] = size[root];
    size[root] += size[a];
  }

  public static void hero() throws IOException {
    int n = nextInt();
    for (int i = 0; i < 30001; i++) {
      fa[i] = i;
      size[i] = 1;
    }
    for (int i = 0; i < n; i++) {
      String s = next();
      char c = s.charAt(0);
      int a = nextInt();
      int b = nextInt();
      if (c == 'M') {
        merge(a, b);
        findSet(1);  //预防极端情况爆栈
      } else {
        if (findSet(a) != findSet(b)) {
          System.out.println(-1);
        } else {
          System.out.println(Math.abs(w[a] - w[b]) - 1);
        }
      }
    }
  }
}

Parity Game

[P5937 CEOI1999]Parity Game - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路 :理解奇偶性传递,利用边带权

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

public class Main {

  static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
  static StreamTokenizer in = new StreamTokenizer(reader);

  static String next() throws IOException {
    in.nextToken();
    return in.sval;
  }

  /**
   * 获取数字
   */
  static int nextInt() throws IOException {
    in.nextToken();
    return (int) in.nval;
  }

  static double nextDouble() throws IOException {
    in.nextToken();
    return in.nval;
  }

  public static void main(String[] args) throws IOException {
    parity();
  }

  static class Node {

    int l;
    int r;
    int ans;  //1奇0偶

    public Node(int l, int r, int ans) {
      this.l = l;
      this.r = r;
      this.ans = ans;
    }
  }

  static int[] pa = new int[10010];
  static int[] d = new int[10010];  //0 表示x和fa【x】奇偶性相同   or 1

  public static int get(int x) {
    if (x == pa[x]) {
      return x;
    }
    int root = get(pa[x]);
    d[x] ^= d[pa[x]];
    return pa[x] = root;
  }

  public static void parity() throws IOException {
    HashMap<Integer, Integer> map = new HashMap<>();
    int len = nextInt();
    int n = nextInt();
    List<Node> nodes = new ArrayList<>();
    HashSet<Integer> set = new HashSet<>();
    for (int i = 0; i < n; i++) {
      int l = nextInt();
      int r = nextInt();
      set.add(l - 1);
      set.add(r);
      int ans = 1;
      String s = next();
      if (s.equals("even")) {
        ans = 0;
      }
      nodes.add(new Node(l, r, ans));
    }
    int[] pos = new int[set.size()];
    int cur = 0;
    Iterator<Integer> iterator = set.iterator();
    while (iterator.hasNext()) {
      pos[cur++] = iterator.next();
    }
    Arrays.sort(pos);
    for (int i1 = 0; i1 < pos.length; i1++) {
      map.put(pos[i1], i1);
      pa[i1] = i1;
    }
    cur = 0;
    while (cur < n) {
      Node node = nodes.get(cur);
      int x = map.get(node.l - 1);
      int y = map.get(node.r);
      int p = get(x), q = get(y);
      if (p == q) {
        if ((d[x] ^ d[y]) != node.ans) {
          break;
        }
      } else {
        pa[p] = q;
        d[p] = d[x] ^ d[y] ^ node.ans;  //ans=dp[p]^d[x]^d[y] 画图理解
      }
      cur++;
    }
    System.out.println(cur);
  }
}

食物链

[P2024 NOI2001] 食物链 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

“扩展域”解法

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

public class Main {

  static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
  static StreamTokenizer in = new StreamTokenizer(reader);

  static String next() throws IOException {
    in.nextToken();
    return in.sval;
  }

  /**
   * 获取数字
   */
  static int nextInt() throws IOException {
    in.nextToken();
    return (int) in.nval;
  }

  static double nextDouble() throws IOException {
    in.nextToken();
    return in.nval;
  }

  public static void main(String[] args) throws IOException {
    P2024();
  }

  static int[] root;

  public static void P2024() throws IOException {
    int n = nextInt();
    int k = nextInt();
    int ans = 0;
    List<Item> list = new ArrayList<>();
    HashSet<Integer> set = new HashSet<>();
    HashMap<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < k; i++) {
      int type = nextInt();
      int x = nextInt();
      int y = nextInt();
      if (x > n || y > n) {
        ans++;
        continue;
      }
      set.add(x);
      set.add(y);
      list.add(new Item(type, x, y));
    }
    Iterator<Integer> iterator = set.iterator();
    int cur = 0;
    while (iterator.hasNext()) {
      map.put(iterator.next(), cur++);
    }
    root = new int[(cur + 1) * 3];
    for (int i = 0; i < root.length; i++) {
      root[i] = i;
    }
    for (int i = 0; i < list.size(); i++) {
      Item item = list.get(i);
      int x_self = map.get(item.x);
      int x_eat = map.get(item.x) + cur;
      int x_enemy = map.get(item.x) + 2 * cur;
      int y_self = map.get(item.y);
      int y_eat = map.get(item.y) + cur;
      int y_enemy = map.get(item.y) + 2 * cur;
      if (item.type == 1) {
        if (get1(x_self) == get1(y_eat) || get1(x_eat) == get1(y_self)) {
          ans++;
          continue;
        }
        root[get1(x_self)] = get1(y_self);
        root[get1(x_eat)] = get1(y_eat);
        root[get1(x_enemy)] = get1(y_enemy);
      } else {
        if (get1(x_self) == get1(y_self) || get1(x_self) == get1(y_eat)) {
          ans++;
          continue;
        }
        root[get1(x_self)] = get1(y_enemy);
        root[get1(x_eat)] = get1(y_self);
        root[get1(x_enemy)] = get1(y_eat);
      }
    }
    System.out.println(ans);
  }

  static int get1(int x) {
    if (root[x] == x) {
      return x;
    }
    return root[x] = get1(root[x]);
  }

  static class Item {

    int type;
    int x;
    int y;

    public Item(int type, int x, int y) {
      this.type = type;
      this.x = x;
      this.y = y;
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值