一、问题描述:
请你设计一个算法,找出一对互斥集合 A 和 B,使得 A和 B 包含的列的总数最大。
输入格式:
一个 1000 行 20 列的矩阵 matrix 作为测试用例,每行输入 20 列的元素,对矩阵找出满足题意的互斥集合。
输出格式:
输出两行,使得 A 的元素+B 的元素个数最大
第一行输出 A 集合中的所有元素(下标从 0 开始),以空格分开
第二行输出 B 集合中的所有元素,格式同上
如果,没有找到非空集合 A 和 B,则输出两行空行
二、代码实现:
#include<iostream>
using namespace std;
const int M = 25, N = 1010;
bool judge[M][M]; //记录两列是否矛盾
int a[N][M], choice[M]; //a数组记录输入
int sum[M]; //存储列的和
int ares = -1, bres = -1; //存储A,B的最优选择
//asum,bsum是当前的A,B的元素和
//eps是列数差的绝对值,tc是当前最大列数和
int asum, bsum, eps = 100, tc;
//判断两列是否互斥
bool dispose(int x, int y)
{
for (int i = 0; i < 1000; i++)
if (a[i][x] + a[i][y] == 2)return false;
return true;
}
//预处理judge[][]数组判断两列是否互斥
void init()
{
for (int i = 0; i < 20; i++)
for (int j = i + 1; j < 20; j++)
if (dispose(i, j))judge[i][j] = judge[j][i] = true;
}
//得到choice[]数组
void get_choice()
{
for (int i = 0; i < 20; i++)
{
int sum = 0;
for (int j = 0; j < 20; j++)
if (judge[i][j])sum += 1 << j;
choice[i] = sum;
}
}
//lowbit函数
int lowbit(int x)
{
return x & (-x);
}
//返回二进制中1的个数
int get_one(int x)
{
int res = 0;
while (x)x -= lowbit(x), res++;
return res;
}
//预处理所有列的和
void get_c()
{
for (int i = 0; i < 20; i++)
{
int res = 0;
for (int j = 0; j < 1000; j++)
res += a[j][i];
sum[i] = res;
}
}
//得到数x二进制表示中所有列的和
int get_sum(int x)
{
int k = 0, res = 0;
while (x)
{
if (x & 1)res += sum[k];
k++;
x >>= 1;
}
return res;
}
//输出答案
void get_answer(int x)
{
int k = 0, len = 20;
while (len--)
{
if (x & 1)
cout << k << " ";
k++;
x >>= 1;
}
cout << endl;
}
//同列数和列数差时比较排序次序
//返回true表示x应在y之后
bool compare(int x, int y)
{
while (x && y)
{
if (lowbit(x) == lowbit(y))
{
x -= lowbit(x), y -= lowbit(y);
continue;
}
else
{
if (lowbit(x) > lowbit(y))return true;
return false;
}
}
return false;
}
//更新函数
void update(int i, int j)
{
ares = i, bres = j;
tc = asum + bsum;
eps = asum - bsum;
}
int main()
{
for (int i = 0; i < 1000; i++)
for (int j = 0; j < 20; j++)
cin >> a[i][j];
//预处理choice[]数组,judge[]数组,sum[]数组
init(), get_choice(), get_c();
//遍历A的所有选择
for (int i = 1; i < 1 << 20; i++)
{
int temp = i, k = 0, j = -1;//j初始化-1是因为-1是111...111
while (temp)
{
if (temp & 1)//如果这一位是1
j = (j & choice[k]);//j和这一位的选择作&
temp >>= 1;
k++;
}
//得到i,j选择的列数
asum = get_one(i), bsum = get_one(j);
if (asum >= bsum && bsum)
{
if (asum + bsum > tc)//如果列数更大
update(i, j);
else if (asum + bsum == tc)//如果列数一样
{
//如果列数差的绝对值更小则更新
if (asum - bsum < eps)
update(i, j);
else if (asum - bsum == eps)//如果列数差相等
{
if (compare(ares, i))
update(i, j);
else if(get_sum(ares) >= get_sum(bres) && get_sum(i) < get_sum(j))
update(i, j);
}
}
}
}
if (~ares)//ares!=-1表示存在非空集合,~是取反,~(-1)=0
get_answer(ares),get_answer(bres);
else cout << endl << endl;
return 0;
}
测试用例由于篇幅问题,请在资源中查找。输入用例和输出结果都有。