java编写匈牙利算法解决最优指派问题

来源:互联网 发布:五十知天命通俗的意思 编辑:程序博客网 时间:2024/06/03 04:19

匈牙利算法是一种常见的最优指派问题,问题描述如下:

实际中,会遇到这样的问题,有n项不同的任务,需要n个人分别完成其中的1项,每个人完成任务的时间不一样。于是就有一个问题,如何分配任务使得花费时间最少。通俗来讲,就是n*n矩阵中,选取n个元素,每行每列各有1个元素,使得和最小。如下图:

指派问题的最优解有这样一个性质,若从矩阵的一行()各元素中分别减去该行()的最小元素,得到归约矩阵,其最优解和原矩阵的最优解相同.


匈牙利算法的具体步骤可以参见很多资料:

  • 匈牙利算法步骤介绍
  • 匈牙利算法PPT
这里给出匈牙利算法解决最优指派问题的java实现:

为了解决可能出现的多解问题,算法加入一个随机变量,随机选择一种最优解当做最终结果。

package test;/** * 匈牙利算法解决最优指派问题 * @author Hao * */public class Hungary {public static void appoint(double[][] m){int N = m.length;//行规约for(int i = 0;i < N;i ++){double min = Double.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 ++){double min = Double.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;}printM(m);//进行试分配while(true){boolean zeroExist = true;while(zeroExist){zeroExist = false;if(rowAppoint(m))zeroExist = true;if(colAppoint(m))zeroExist = true;printM(m);}//判断是否达到最优分配if(isOptimal(m))break;//变换矩阵updataM(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;}printM(m);}}public static void updataM(double[][] 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;}}}}}//寻找盖零线以外最小的数double min = Double.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];}}}}//打钩各行减去minfor(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;}}}//打钩各列加上minfor(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(double[][] 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元素返回truepublic static boolean rowAppoint(double[][] 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.95){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元素返回truepublic static boolean colAppoint(double[][] 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;}public static void main(String[] args) {double[][] m = new double[][]{{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}};appoint(m);printResult(m);}public static void printM(double[][] m){System.out.println("---------------");for(int i = 0;i < m.length;i ++){for(int j = 0;j < m.length;j ++)System.out.print(m[i][j] + " ");System.out.println();}}public static void printResult(double[][] m){System.out.println("-----Result------");for(int i = 0;i < m.length;i ++){for(int j = 0;j < m.length;j ++)if(m[i][j] == -1)System.out.print(i+"--"+j+", ");}}}

2 0
原创粉丝点击