编写程序求解如下问题:
某商业公司计划开办5家新商店,公司决定由5家建筑公司分别承建。已知建筑公司Ai 对新商店Bj 的建造费用报价如下表所示。问:商业公司应对5家建筑公司如何分配建造任务,使得总建造费用最小?
源代码:
//指派问题的匈牙利解法:
package intGh;
public class zhipaiWT {
public static void assign(int[][] m) {
int N = m.length;
// 行规约
for (int i = 0; i < N; i++) {
int min = Integer.MAX_VALUE;
for (int j = 0; j < N; j++) {
if (m[i][j] < min)
min = m[i][j];
}
for (int j = 0; j < N; j++)
m[i][j] -= min;
}
// 列规约
for (int j = 0; j < N; j++) {
int min = Integer.MAX_VALUE;
for (int i = 0; i < N; i++) {
if (m[i][j] < min)
min = m[i][j];
}
if (min == 0)
continue;
for (int i = 0; i < N; i++)
m[i][j] -= min;
}
print_m(m);
// 进行试分配
while (true) {
boolean zeroExist = true;
while (zeroExist) {
zeroExist = false;
if (rAssign(m))
zeroExist = true;
if (cAssign(m))
zeroExist = true;
print_m(m);
}
// 判断是否达到最优分配
if (isOptimal(m))
break;
// 变换矩阵
updM(m);
// 将0元素恢复
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++)
if (m[i][j] < 0)
m[i][j] = 0;
}
print_m(m);
}
}
public static void updM(int[][] m) {
int N = m.length;
// 记录行、列是否打钩
boolean[] rowIsChecked = new boolean[N];
boolean[] colIsChecked = new boolean[N];
// 给没有被圈的行打钩
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (m[i][j] == -1) {
rowIsChecked[i] = false;
break;
} else {
rowIsChecked[i] = true;
}
}
}
boolean isChecked = true;
while (isChecked) {
isChecked = false;
// 对所有打钩行的0元素所在列打钩
for (int i = 0; i < N; i++) {
if (rowIsChecked[i]) {
for (int j = 0; j < N; j++) {
if (m[i][j] == -2 && !colIsChecked[j]) {
colIsChecked[j] = true;
isChecked = true;
}
}
}
}
// 对打钩列上的独立零元素行打钩
for (int j = 0; j < N; j++) {
if (colIsChecked[j]) {
for (int i = 0; i < N; i++) {
if (m[i][j] == -1 && !rowIsChecked[i]) {
rowIsChecked[i] = true;
isChecked = true;
}
}
}
}
}
// 寻找盖零线以外最小的数
int min = Integer.MAX_VALUE;
for (int i = 0; i < N; i++) {
if (rowIsChecked[i]) {
for (int j = 0; j < N; j++) {
if (!colIsChecked[j]) {
if (m[i][j] < min)
min = m[i][j];
}
}
}
}
// 打钩各行减去min
for (int i = 0; i < N; i++) {
if (rowIsChecked[i]) {
for (int j = 0; j < N; j++) {
if (m[i][j] > 0)
m[i][j] -= min;
}
}
}
// 打钩各列加上min
for (int j = 0; j < N; j++) {
if (colIsChecked[j]) {
for (int i = 0; i < N; i++) {
if (m[i][j] > 0)
m[i][j] += min;
}
}
}
}
// 统计被圈起来的0数量,判断是否找到最优解
public static boolean isOptimal(int[][] m) {
int count = 0;
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m.length; j++)
if (m[i][j] == -1)
count++;
}
return count == m.length;
}
// 寻找只有一个0元素的行,将其标记为独立0元素(-1),对其所在列的0元素画叉(-2)
// 若存在独立0元素返回true
public static boolean rAssign(int[][] m) {
boolean zeroExist = false;
int N = m.length;
// 寻找只有一个0元素的行(列)
for (int i = 0; i < N; i++) {
int zeroCount = 0;
int colIndex = -1;
for (int j = 0; j < N; j++) {
if (m[i][j] == 0) {
zeroCount++;
colIndex = j;
zeroExist = true;
}
}
// 将独立0元素标记为-1(被圈起来),对应的列上的零标记为-2(被划去)
if (zeroCount == 1) {
m[i][colIndex] = -1;
for (int k = 0; k < N; k++) {
if (k == i)
continue;
else if (m[k][colIndex] == 0)
m[k][colIndex] = -2;
}
} else if (zeroCount == 2) {// 如果存在2组解,随机选择其一标记
if (Math.random() > 0.5) {
m[i][colIndex] = -1;
for (int k = 0; k < N; k++) {
if (k == i)
continue;
else if (m[k][colIndex] == 0)
m[k][colIndex] = -2;
}
for (int j = 0; j < N; j++) {
if (j == colIndex)
continue;
else if (m[i][j] == 0)
m[i][j] = -2;
}
}
}
}
return zeroExist;
}
// 寻找只有一个0元素的列,将其标记为独立0元素(-1),对其所在行的0元素画叉(-2)
// 若存在独立0元素返回true
public static boolean cAssign(int[][] m) {
boolean zeroExist = false;
int N = m.length;
for (int j = 0; j < N; j++) {
int zeroCount = 0;
int rowIndex = -1;
for (int i = 0; i < N; i++) {
if (m[i][j] == 0) {
zeroCount++;
rowIndex = i;
zeroExist = true;
}
}
if (zeroCount == 1) {
m[rowIndex][j] = -1;
for (int k = 0; k < N; k++) {
if (k == j)
continue;
else if (m[rowIndex][k] == 0)
m[rowIndex][k] = -2;
}
}
}
return zeroExist;
}
static int cost[] = new int[5];
public static void printResult(int[][] m, int[][] M) {
System.out.print("\n最优指派为:\n");
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m.length; j++)
if (m[i][j] == -1) {
int ii = i + 1;
int jj = j + 1;
System.out.print("公司A" + ii + "承建工程B" + jj + "\n");
totalCost += M[i][j];
cost[i] = M[i][j];
}
}
}
static int num = 1;
public static void print_m(int[][] m) {
System.out.printf("\n第%d次变换后系数矩阵变为:\n", num);
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m.length; j++)
System.out.print(m[i][j] + "\t");
System.out.println();
}
num++;
}
static int totalCost = 0;
// 主函数
public static void main(String[] args) {
int[][] m = new int[][] { { 4, 8, 7, 15, 12 }, { 7, 9, 17, 14, 10 }, { 6, 9, 12, 8, 7 }, { 6, 7, 14, 6, 10 },
{ 6, 9, 12, 10, 6 } };
int[][] M = new int[5][5];
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
M[i][j] = m[i][j];
assign(m);
printResult(m, M);
System.out.println("\n总建造费用为:" + cost[0] + "+" + cost[1] + "+" + cost[2] + "+" + cost[3] + "+" + cost[4] + "="
+ totalCost);
}
}