算法实验(四)——回溯/分支限界法

一、问题描述:

请你设计一个算法,找出一对互斥集合 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;
}

测试用例由于篇幅问题,请在资源中查找。输入用例和输出结果都有。

猜你喜欢

转载自blog.csdn.net/aimat2020/article/details/124762236