public class AssignWorkProblem {
public static void main(String[] args) {
/*
*测试
**/
int[][] cost=new int[][]{{2,15,13,4},{10,4,14,15},{9,14,16,13},{7,8,11,9}};
System.out.println(Arrays.toString(awpProcedure(cost, 4, 4)));
cost=new int[][]{{12,7,9,7,9},{8,9,6,6,6},{7,17,12,14,12},{15,14,6,6,10},{4,10,7,10,6}};
System.out.println(Arrays.toString(awpProcedure(cost, 5, 5)));
}
/*
* 费用矩阵costMatrix,由于要改变costMatrix的值,clone方法只能对基本类型;
* pnum即为几个人,也是costMatrix的行数,wnum是几个任务,也是costMatrix的列数
* 返回值:没两个数字为一组,表示i,j。如返回[1,1,2,0]表示costMatrix[1][1]、costMatrix[2][0]费用最低
*/
public static int[] awpProcedure(int[][] costMatrix,int pnum,int wnum){
if(pnum<1||pnum
int[][] costC=new int[pnum][]; //clone 一份costMatrix
for(int i=0;i
}
//每行减去最小的元素
int[] lzeroa=new int[pnum+1]; //记录每行0的个数,lzero[pnum]记录0最少的行标
lzeroa[pnum]=-1;
int i,j;
for(i=0;i
for(j=1;j
for(j=0;j
lzeroa[i]+=costC[i][j]==0?1:0;
}
}
for(j=0;j
for(i=1;i
if(cmin==0)continue;
for(i=0;i
lzeroa[i]+=costC[i][j]==0?1:0;
}
}
int[] rzerop;
int whilenum=0;
while(true){
boolean[] lzerob=new boolean[pnum]; //记录某行是否查找过
rzerop=new int[pnum*2]; //记录0元素所在的行列
Arrays.fill(rzerop, -1);
if(awpIsSolution(costC,pnum,wnum,lzeroa.clone(),lzerob,rzerop))
break;
//下面调整矩阵
int[] coverLC=new int[pnum+wnum]; //要被标记的行列,0-pnum-1为行,pnum以后为列
Arrays.fill(coverLC, -1);
//没有找到合适0元素的行做标记
for(i=0;i
//对已经标记的行上的0元素所在的列做标记
for(i=0;i
for(j=0;j
coverLC[pnum+j]=j;
}
}
//对已经标记的列上的已经选中的0元素所在的行做标记
for(j=0;j
for(i=0;i
coverLC[rzerop[i]]=rzerop[i];
}
}
}
//确定能找出新最小值的区域,直线覆盖掉没有打勾的行,打勾的列,最终coverLC[x]!=-1就是能选择的数
for(i=0;i
else coverLC[pnum+i]=i;
}
//从区域中找出最小元素
int nmin=-1;
for(i=0;i
for(j=0;j
if(nmin==-1)nmin=costC[i][j];
else nmin=nmin>costC[i][j]?costC[i][j]:nmin;
}
}
//打勾的列加上nmin,打勾的行减去nmin,记录0个数的数组作相应变化
for(j=0;j
for(i=0;i
costC[i][j]+=nmin;
}
}
}
for(i=0;i
for(j=0;j
if(costC[i][j]==0)lzeroa[i]+=1;
}
}
}
whilenum++;
if(whilenum==100){
System.out.println("100次之内矩阵调整没有找到");
return null;
}
}
return rzerop;
}
/*
* 测试矩阵costC是否有解,已经通过变换或者调整得到的矩阵
*/
public static boolean awpIsSolution(int[][] costC,int pnum,int wnum,int[] lzeroa,boolean[] lzerob,int[] rzerop){
int i,j,rzeropi=0;
for(int p=0;p
for(i=0;i
if(lzeroa[pnum]!=-1&&lzeroa[i]
}
//没有找到足够的不在同一行同一列的0元素,需要对矩阵进行调整,如果lzeroa[pnum]有值,则说明该行一定能找到
if(lzeroa[pnum]==-1){
return false;
}
//划去找到的行中没有被覆盖的0元素所在的行列
for(j=0;j
//第一次找0元素最少的行
if(rzeropi==0){
rzerop[rzeropi++]=lzeroa[pnum];
rzerop[rzeropi++]=j;
lzerob[lzeroa[pnum]]=true; //找到第lzeroa[pnum]行,第j列0元素
//划去所在的行列时 lzeroa做相应的变化
for(i=0;i
lzeroa[i]-=1;
}
lzeroa[pnum]=-1;
break;
}
//找到的0元素是否被划去
for(i=0;i
if(i
rzerop[rzeropi++]=j;
lzerob[lzeroa[pnum]]=true;
for(i=0;i
lzeroa[i]-=1;
}
lzeroa[pnum]=-1;
break;
}
}
return true;
}
}
哇好复杂啊··