#include <iostream>
using namespace std;
#define Max 5 //维数
int orgin[Max][Max]; //原始矩阵
int sub[Max][Max]; //归约矩阵
int draw[Max][Max]; //0:未被画线 1:画了1次 2: 画了2次(交点)
int row0[Max], col0[Max]; //行列0计数器; 记录行列0个数
int dep0[Max][Max]; //元素类型记录器; 0:非0元素 1:非独立0元素 2:独立0元素
int hookX[Max], hookY[Max]; //画线时是否被打勾,1是0不是
void printArray(int arr[][Max], int x, int y) {
for (int i = 0; i < x; ++i) {
for (int j = 0; j < y; ++j) {
cout << arr[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
void print(int* arr, int len) {
for (int i = 0; i < len; ++i) {
cout << arr[i] << " ";
}
cout << endl;
}
//计算行列0个数
void countZero() {
//重置行列0计数器,元素类型记录器
memset(row0, 0, sizeof(int) * Max);
memset(col0, 0, sizeof(int) * Max);
memset(dep0, 0, sizeof(int) * Max * Max);
for (int i = 0; i < Max; ++i) {
for (int j = 0; j < Max; ++j) {
if (sub[i][j] == 0) {
row0[i]++;
col0[j]++;
}
}
}
}
int drawLine() {
//重置划线记录,行列勾选记录
for (int i = 0; i < Max; ++i) {
for (int j = 0; j < Max; ++j) {
draw[i][j] = 0;
}
hookX[i] = 1;
hookY[i] = 0;
}
//对不含独立0元素的行打勾,这里是含独立0去勾
for (int i = 0; i < Max; ++i) {
for (int j = 0; j < Max; ++j) {
if (dep0[i][j] == 2) {
hookX[i] = 0;
break;
}
}
}
bool isStop = false;
while (!isStop) {
isStop = true;
//hookY 对打勾的行中含0元素的未打勾的列打勾
for (int i = 0; i < Max; ++i) {
if (hookX[i] == 1) {
for (int j = 0; j < Max; ++j) {
if (sub[i][j] == 0 && hookY[j] == 0) {
hookY[j] = 1;
isStop = false;
}
}
}
}
//hookX 对打勾的列中含独立0元素的未打勾的行打勾
for (int i = 0; i < Max; ++i) {
if (hookY[i] == 1) {
for (int j = 0; j < Max; ++j) {
if (sub[j][i] == 0 && dep0[j][i] == 2 && hookX[j] == 0) {
hookX[j] = 1;
isStop = false;
}
}
}
}
}
//没有打勾的行和有打勾的列画线,这就是覆盖所有0元素的最少直线数
int line = 0;
for (int i = 0; i < Max; ++i) {
if (hookX[i] == 0) {
for (int j = 0; j < Max; ++j) {
draw[i][j]++;
}
line++;
}
if (hookY[i]) {
for (int j = 0; j < Max; ++j) {
draw[j][i]++;
}
line++;
}
}
return line;
}
//试指派,标记独立0元素位置
/*1.找含0最少(至少一个)的那一行/列 2.划掉,更新该行/列0元素所在位置的row[],col[]
3.直到所有0被划线,这里定义为row[]col[]全为INT_MAX,表示该行/列无0元素*/
int find() {
// printArray(sub, Max, Max);
countZero();
int zero = 0;
while (1) {
for (int i = 0; i < Max; ++i) {
//至少一个0, 将0个设置为INT_MAX避免重复
if (row0[i] == 0) {
row0[i] = INT_MAX;
}
if (col0[i] == 0) {
col0[i] = INT_MAX;
}
}
bool stop = true;
// 找含0最少(至少一个)的那一行/列
if (*min_element(row0, row0 + Max) <= *min_element(col0, col0 + Max)) {
int min = row0[0], rowId = 0;
for (int i = 1; i < Max; ++i) {
if (min > row0[i]) {
min = row0[i];
rowId = i;
}
}
int colId = -1;
for (int i = 0; i < Max; ++i) {
if (sub[rowId][i] == 0 && col0[i] != INT_MAX) {
colId = i;
stop = false;
zero++;
break;
}
}
if (stop) {
break;
}
//所在行列上的0划去
row0[rowId] = col0[colId] = INT_MAX;
dep0[rowId][colId] = 1;
//更新独立0所在的行/列上的0对应的未被划线的行列0的计数
for (int i = 0; i < Max; ++i) {
if (sub[rowId][i] == 0 && col0[i] != INT_MAX) {
col0[i]--;
}
if (sub[i][colId] == 0 && row0[i] != INT_MAX) {
row0[i]--;
}
}
} else {
int min = col0[0], colId = 0;
for (int i = 1; i < Max; ++i) {
if (min > col0[i]) {
min = col0[i];
colId = i;
}
}
int rowId = -1;
for (int i = 0; i < Max; ++i) {
if (sub[i][colId] == 0 && row0[i] != INT_MAX) {
rowId = i;
stop = false;
zero++;
break;
}
}
if (stop) {
break;
}
row0[rowId] = col0[colId] = INT_MAX;
dep0[rowId][colId] = 1;
for (int i = 0; i < Max; ++i) {
if (sub[rowId][i] == 0 && col0[i] != INT_MAX) {
col0[i]--;
}
if (sub[i][colId] == 0 && row0[i] != INT_MAX) {
row0[i]--;
}
}
}
}
//r[i][j] 0:非0元素 1:非独立0元素 2:独立0元素
for (int i = 0; i < Max; ++i)
for (int j = 0; j < Max; ++j)
if (sub[i][j] == 0)
dep0[i][j]++;
return zero;
}
//初始化数据
void init() {
int orginData[Max][Max] = { { 12, 7, 9, 7, 9 }, { 8, 9, 6, 6, 6 }, { 7, 17,
12, 14, 9 }, { 15, 14, 6, 6, 10 }, { 4, 10, 7, 10, 9 } };
for (int i = 0; i < Max; ++i) {
for (int j = 0; j < Max; ++j) {
orgin[i][j] = orginData[i][j];
}
}
}
//行列规约
void subRowAndCol() {
//行规约
for (int i = 0; i < Max; ++i) {
for (int j = 0; j < Max; ++j) {
//求和最大
//sub[i][j] = *max_element(orgin[i], orgin[i]+Max)-orgin[i][j];
//求和最小
sub[i][j] = orgin[i][j] - *min_element(orgin[i], orgin[i] + Max);
}
}
//列规约
for (int i = 0; i < Max; ++i) {
int min = sub[0][i];
for (int j = 0; j < Max; ++j) {
if (min > sub[j][i]) {
min = sub[j][i];
}
}
for (int j = 0; j < Max; ++j) {
sub[j][i] -= min;
}
}
}
//找出独立0元素和划线
void findDependent0() {
//找到足够独立0元素即可
while (find() < Max) {
//当前矩阵无法满足,划线
drawLine();
int min = INT_MAX;
//未被划线数的最小值
for (int i = 0; i < Max; ++i) {
for (int j = 0; j < Max; ++j) {
if (draw[i][j] == 0 && min > sub[i][j]) {
min = sub[i][j];
}
}
}
for (int i = 0; i < Max; ++i) {
for (int j = 0; j < Max; ++j) {
if (draw[i][j] == 0) { //将所有未被划线的数减去最小值
sub[i][j] -= min;
} else if (draw[i][j] == 2) { //2次划线数加上最小值
sub[i][j] += min;
}
}
}
}
}
int main() {
init();
subRowAndCol();
findDependent0();
int result = 0;
for (int i = 0; i < Max; ++i) {
for (int j = 0; j < Max; ++j) {
//独立0位置对应值之和为解
if (dep0[i][j] == 2) {
cout << orgin[i][j] << " ";
result += orgin[i][j];
}
}
}
cout << " = " << result << endl;
return 0;
}
匈牙利指派问题
最新推荐文章于 2023-08-08 17:02:43 发布