回溯/分支界限法
问题描述
给定1个1000行×20列的0-1矩阵,对于该矩阵的任意1列,其中值为1的元素的数量不超过10%。设有两个非空集合A和B,每个集合由矩阵的若干列组成。集合A和B互斥是指对于矩阵的任意一行,同时满足下列2个条件:1)若A中有一个或多个元素在这一行上的值是1,则B中的元素在这一行全部是0;2)若B中有一个或多个元素在这一行上的值是1,则A中的元素在这一行全部是0。请你设计一个算法,找出一对互斥集合A和B,使得A和B包含的列的总数最大。
代码主体来自
(7条消息) 最大0-1互斥矩阵——回溯法_volcanical的博客-CSDN博客_互斥矩阵
减少时间的方法来自
(7条消息) 回溯分支界限法——矩阵互斥——含测试用例_roman_bu_tic的博客-CSDN博客_矩阵互斥集合
利用judge数组,舍弃了forbiden数组,从两个数组大小即可看出极大减少了运算次数
#include <iostream>
#include <fstream>
using namespace std;
class Matrix {
private:
int array[1000][20];
int belong[20]; //1属于A 2属于B
int solve[20]; //解
int forbiden[1000]; //1代表被A占据 2代表被B占据
int best; //最佳解
int sumA; //记录现在最佳
int sumB;
int cha;
int judge[20][20]; //减少时间的关键,判断两列互斥
public:
Matrix(istream &afile) {
for (int i = 0; i < 1000; i++) {
for (int j = 0; j < 20; j++)
afile >> array[i][j];
}
for (int i = 0; i < 20; i++) {
belong[i] = 0;
solve[i] = 0;
}
for (int i = 0; i < 1000; i++) {
forbiden[i] = 0;
}
for(int i=0;i<20;i++)
{
for(int j=0;j<20;j++)
{
judge[i][j]=1;
judge[j][i]=1;
for(int k=0;k<1000;k++)
{
if(array[k][i]==1 && array[k][j]==1)
{
judge[i][j]=0;
judge[j][i]=0;
break;
}
}
}
}
best = 0;
sumA = 0;
sumB = 0;
cha = 0;
}
int CouldBeA(int j) { //第j列能否成为A
for (int i = 0; i < 20; i++) {
if (belong[i]==2&&judge[i][j]==0)
return 0;
}
return 1;
}
int CouldBeB(int j) { //第j列能否成为B
for (int i = 0; i < 20; i++) {
if (belong[i]==1&&judge[i][j]==0)
return 0;
}
return 1;
}
void biggestdivide(int i) {
if (i > 19) {
if (sumA + sumB >= best && sumA && sumB && sumA > sumB && sumA - sumB > cha) {
best = sumA + sumB;
cha = sumA - sumB;
for (int i = 0; i < 20; i++) {
solve[i] = belong[i];
}
}
return;
}
if (CouldBeA(i)) { //第I列当成A
// int change[1000];
// for (int j = 0; j < 1000; j++) {
// change[j] = forbiden[j]; //原来的forbiden_B
// }
// for (int j = 0; j < 1000; j++) {
// if (array[j][i] == 1) {
// forbiden[j] = 1;
// }
// }
belong[i] = 1;
sumA++;
biggestdivide(i + 1);
// for (int j = 0; j < 1000; j++) {
// forbiden[j] = change[j];
// }
belong[i] = 0;
sumA--;
}
if (CouldBeB(i)) {
// int change[1000];
// for (int j = 0; j < 1000; j++) {
// change[j] = forbiden[j]; //原来的forbiden_A
// }
// for (int j = 0; j < 1000; j++) {
// if (array[j][i] == 1) {
// forbiden[j] = 2;
// }
// }
belong[i] = 2;
sumB++;
biggestdivide(i + 1);
// for (int j = 0; j < 1000; j++) {
// forbiden[j] = change[j];
// }
belong[i] = 0;
sumB--;
}
if (sumA + sumB + 19 - i >= best)
biggestdivide(i + 1);
}
void show() {
for (int i = 0; i < 20; i++) {
if (solve[i] == 1)
cout << i << ' ' ;
}
cout << endl;
for (int i = 0; i < 20; i++) {
if (solve[i] == 2)
cout << i << ' ';
}
cout << endl;
}
};
int main() {
// ifstream aflie;
// aflie.open("exp4_data.txt", ios::in);
Matrix test(cin);
test.biggestdivide(0);
test.show();
// aflie.close();
return 0;
}