AcWing 1243. 糖果
原题链接
中等
作者:
热的干面
,
2023-01-25 21:59:25
,
所有人可见
,
阅读 21
package lanqiao_day01;
import java.io.*;
import java.math.BigDecimal;
import java.time.Year;
import java.util.*;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer in = new StreamTokenizer(br);
static PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));
public static int nextInt() throws Exception {
in.nextToken();
return (int) in.nval;
}
public static double nextDouble() throws Exception {
in.nextToken();
return in.nval;
}
public static long nextLong() throws Exception {
in.nextToken();
return (long) in.nval;
}
public static String nextStr() throws Exception {
in.nextToken();
return in.sval;
}
public static String nextLine() throws Exception {
return br.readLine();
}
static int n, m, k;
static int N = 110;
static int M = 1 << 20;
static int[] log2 = new int[M];
//使用set去重,优化速度
static Set[] col = new HashSet[21];
public static void main(String[] args) throws Exception {
n = nextInt();
m = nextInt();
k = nextInt();
for (int i = 0; i < m; i++) {
col[i] = new HashSet<Integer>();
//获取log2数组,方便之后根据数值得出二进制上哪一位是1
log2[1 << i] = i;
}
//读取n个糖果
for (int i = 0; i < n; i++) {
//使用二进制数表示当前这包糖果有哪些糖
int state = 0;
for (int j = 0; j < k; j++) {
int c = nextInt();
//或运算,例如c=5,1左移四位之后对state进行或运算,state的第五个数会变为1
state |= 1 << c - 1;
}
//当前这包糖果已经全部读完,将其添加到对应的糖果种类数组里
for (int k = state; k > 0; k -= lowbit(k)) {//lowbit返回最后一个1的数值,例如10010会返回10
int c = log2[lowbit(k)];//获取对应哪一种糖果
col[c].add(state);
}
}
//迭代加深,不断去扩大depth的值
int depth = 1;
while (depth <= m && !dfs(depth, 0)) {
depth++;
}
//不满足对应的要求,depth最多取m个,如果大于m个就代表没有结果
if (depth > m)
depth = -1;
System.out.println(depth);
}
//递归过程
private static boolean dfs(int depth, int state) {
//糖果已经拿完或者最少要拿多少包糖果大于当前剩余的数量
if (depth <= 0 || h(state) > depth)
return state == (1 << m) - 1;
int t = -1;
// 找到选择性最小的一列,就是说包含该糖果种类的糖果包数量最少
//(1 << m) - 1 -state代表当前还有那些种类没有选择
for (int i = (1 << m) - 1 -state; i > 0; i -= lowbit(i)) {
int c = log2[lowbit(i)];
//比较长度
if (t == -1 || col[t].size() > col[c].size()) {
t = c;
}
}
// 枚举选哪行
Set<Integer> set = col[t];
for(int row : set) {
if (dfs(depth - 1, state | row)) {
return true;
}
}
return false;
}
// 求一下最少需要再选几包糖果
private static int h(int state) {
int res = 0;
for (int i = (1 << m) - 1 - state; i > 0;) {
int c = log2[lowbit(i)];
res++;
Set<Integer> set = col[c];
for(int row : set) {
i &= ~row;
//将row这包糖果对应的糖果种类中的1全部变成0,~取反,在配上&能实现对应的效果
}
}
return res;
}
private static int lowbit(int i) {
return i & -i;
}
}