题意:给出一些二进制数,有n行p列,判断在p列中,最少几列就能区分这n行的二进制数,注意列数不一定连续。
题解:此题用到了枚举子集,n的子集有2^n - 1个子集,所以就将 0 到 2^n - 1每个数的二进制和给出的二进制按‘与’位运算符运算,如果得到的值未被标记就标记为1,如果已经标记过了,说明这种子集有重复,那么列数的最小值不会存在在这个子集中,否则将子集中的1的个数统计出来,每个子集都统计一次,最后个数最少的数就是最小列数。
#include <stdio.h>
#include <math.h>
#include <string.h>
const int N = 100 + 5;
const int MAX = 40000;
int p, n, s[N], vis[MAX], count, ans, a[N];
void init() {
for (int i = 0; i < N; i++)
s[i] = 0;
for (int i = 0; i < MAX; i++)
vis[i] = 0;
ans = 16;
}
void subset(int j) {
int temp, flag = 1;
for (int i = 0; i < n; i++) {
temp = s[i] & j; //取交集
a[i] = temp;
if (vis[temp]) {
flag = 0;
break;
}
else
vis[temp] = 1;
}
if (flag) {
count = 0;
for (int i = 0; i < p; i++)
if (j & 1 << i)
count++; //统计1的个数
if (count < ans)
ans = count;
}
for (int i = 0; i < N; i++)
vis[a[i]] = 0;
}
int main() {
int cases, a;
scanf("%d", &cases);
while (cases--) {
init();
scanf("%d%d", &p, &n);
for (int i = 0; i < n; i++)
for (int j = p - 1; j >= 0; j--) {
scanf("%d", &a);
s[i] += a * pow(2, j);
}
for (int i = 0; i < (1 << p); i++)
subset(i);
printf("%d\n", ans);
}
return 0;
}