2019牛客暑期多校训练营(第五场) maximum clique 1 (Hungary, 二分图最小点覆盖,二分图最大独立集)

链接:https://ac.nowcoder.com/acm/contest/885/F
来源:牛客网

题目描述

You are given a set S containing n distinct positive integers a_1, a_2 … a_n

Please find a subset of S which has the maximum size while satisfying the following constraint:

The binary representations of any two numbers in this subset must have at least two different bits.

If there are multiple valid subsets, please output any one of them.

输入描述:

在这里插入图片描述

输出描述:

You should output two lines.

The first line contains one integer m denoting the size of the subset you found.

The second line contains m positive integers denoting the member of this subset.

输入

5
3 1 4 2 5

输出

3
4 1 2

说明
3 (112) and 1 (012) has only 1 different bit so they can not be in the set together. 4 (1002) and 1 (0012) has 2 different bits so they can be in the set together.
Following unordered pairs are allowed to be in the set together: <1, 2>, <1, 4>, <2, 4>, <2, 5>, ❤️, 4>, ❤️, 5>. Thus the maximum set is of size 3 ({1, 2, 4}).

Solution

  最大团问题和最大独立集问题是互补的问题 , 两个相异的数至少两个 bit 不一样的否命题就是 : 恰一个 bit 不一样,可发现按照题目叙述所建的图的补图就是刚好是二分图,于是套用二分图最大独立集模板就把这题解决了

 要求原图的最大团 = 补图最大独立集 = (补图的最小点覆盖)的补集
最小点覆盖先求最大匹配,然后任取二分图的一边(记作X),对于X中的所有未匹配的点,从该点出发, 做一条未匹配边->匹配边->未匹配边->……->匹配边(注意最后以匹配边结尾),并且标记用到的点:,二分图的一边(X)取未访问过的点加入集合S,另一边(Y)取访问过的点加入集合S即可。

(求最小点覆盖时,若是从X中选取为匹配的点,则得到的最小点覆盖就要从X边中取未访问过的,从Y边中取访问过的)

更具体的,参考:
https://www.cnblogs.com/jianglangcaijin/p/6035945.html


AC Code

/*
 * Copyright (c) 2019 Ng Kimbing, HNU, All rights reserved. May not be used, modified, or copied without permission.
 * @Author: Ng Kimbing, HNU.
 * @LastModified:2019-08-02 T 10:03:09.012 +08:00
 */

package ACMProblems.GraphProblem;

import java.util.*;

import static ACMProblems.ACMIO.*;

public class Hungary {

    static class Edge {
        int to, next;

        Edge(int to, int next) {
            this.to = to;
            this.next = next;
        }
    }

    private static int MAXN = 5010;//点数的最大值
    private static int MAXM = 50010;//边数的最大值
    private static Edge[] edge = new Edge[MAXM];
    private static int head[] = new int[MAXN];
    private static int[] linker = new int[MAXN];
    private static boolean[] used = new boolean[MAXN];
    private static boolean[] isMatched = new boolean[MAXN];
    private static boolean[] visited = new boolean[MAXN];
    private static int uN;
    private static int tot;

    private static void init() {
        tot = 0;
        Arrays.fill(head, -1);
    }

    private static void addEdge(int u, int v) {
        edge[tot] = new Edge(v, head[u]);
        head[u] = tot++;
    }

    private static boolean dfs(int u) {
        visited[u] = true;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (!used[v]) {
                used[v] = true;
                if (linker[v] == -1 || dfs(linker[v])) {
                    linker[v] = u;
                    return true;
                }
            }
        }
        return false;
    }

    private static void hungary() {
        Arrays.fill(linker, 0, uN, -1);
        for (int u = 0; u < uN; u++) {
            Arrays.fill(used, 0, uN, false);
            if (dfs(u)) {
                isMatched[u] = true;
            }
        }
    }

    private static int count(int bit) {
        int res = 0;
        while (bit != 0) {
            bit = (bit & (bit - 1));
            ++res;
        }
        return res;
    }

    private static Set<Integer> left = new HashSet<>();
    private static Set<Integer> right = new HashSet<>();

    private static void leftOrRight(int i, int k) {
        if ((count(k) & 1) == 1)
            left.add(i);
        else right.add(i);
    }

    public static void main(String[] args) throws Exception {
        uN = nextInt();
        int[] data = new int[uN];
        init();
        for (int i = 0; i < uN; ++i) {
            data[i] = nextInt();
            leftOrRight(i, data[i]);
        }
        for (int i = 0; i < uN; ++i)
            for (int j = i + 1; j < uN; ++j) {
                if (count(data[i] ^ data[j]) == 1) {
                    if (left.contains(i))
                        addEdge(i, j);
                    else
                        addEdge(j, i);

                }
            }
        hungary();
        Arrays.fill(used, 0, uN, false);
        Arrays.fill(visited, 0, uN, false);
        for (int i : left)
            if (!isMatched[i])
                dfs(i);
        int[] res = new int[uN];
        int num = 0;
        for (int l : left)
            if (visited[l])
                res[num++] = data[l];
        for (int r : right)
            if (!used[r])
                res[num++] = data[r];
        out.println(num);
        for (int i = 0; i < num; ++i)
            out.print(res[i] + " ");
        out.println();
        out.flush();
    }
}

Hungary模板

    static class Edge {
        int to, next;

        Edge(int to, int next) {
            this.to = to;
            this.next = next;
        }
    }
    private static int MAXN = 5010;//点数的最大值
    private static int MAXM = 50010;//边数的最大值
    private static Edge[] edge = new Edge[MAXM];
    private static int head[] = new int[MAXN];
    private static int[] linker = new int[MAXN];
    private static boolean[] used = new boolean[MAXN];
    private static boolean[] isMatched = new boolean[MAXN];
    private static boolean[] visited = new boolean[MAXN];
    private static int uN;
    private static int tot;

    private static void init() {
        tot = 0;
        Arrays.fill(head, -1);
    }

    private static void addEdge(int u, int v) {
        edge[tot] = new Edge(v, head[u]);
        head[u] = tot++;
    }

    private static boolean dfs(int u) {
        visited[u] = true;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (!used[v]) {
                used[v] = true;
                if (linker[v] == -1 || dfs(linker[v])) {
                    linker[v] = u;
                    return true;
                }
            }
        }
        return false;
    }

    private static int hungary() {
        int res = 0;
        Arrays.fill(linker, 0, uN, -1);
        for (int u = 0; u < uN; u++) {
            Arrays.fill(used, 0, uN, false);
            if (dfs(u)) {
                ++res;
                isMatched[u] = true;
            }
        }
        return res;
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值