链接: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;
}