一.指派问题
在生活中经常遇见这样的问题,有n项任务要求n个人完成,这n个人完成各项任务的效率(或所需时间)不同,于是产生指派哪个人去完成哪项任务的问题,这类问题称为指派问题或分派问题。
1.指派问题的数学模型
引入变量Xij,其取值只能是1或0,并令Xij=1表示指派第i人完成第j项任务 Xij=0表示不指派第i人完成第j项任务;当问题要求极小化时,数学模型是: Min z=
① ② ③ ④约束条件②说明第j项任务只能由1人完成;约束条件③说明第i人只能完成1项任务。满足约束条件②--④的解,可以用矩阵来表示,此矩阵称为解矩阵。
当问题要求极大化时,可令bij=M-Cij把问题转换成极小化模型。
二.指派问题的解法
指派问题是0-1规划的特例,也是运输问题的特例,可以用整数规划、0-1规划、运输问题的解法去求解,但因忽略指派问题的特殊性,因而不能达到好的解题效率。
指派问题的最优解具有这样的性质,若从系数矩阵(bij)的一行(列)各元素中分别减去该行(列)的最小元素,得到新矩阵(bij’),那么以(bij’)为系数矩阵求得的最优解和用原系数矩阵求得的最优解相同。
库恩于1955年提出了指派问题的解法,其中引用了康尼格一个关于矩阵中0元素的定理:系数矩阵中独立0元素的最多个数等于能覆盖所有0元素的最少直线数。
三.程序实现
依照上述方法,编制Assignment类,以实现对任意规模的指派问题的解决。
class Assignment
{
public:
Assignment(int n, int * eff);
~Assignment();
int * getAssignment(bool isMin=true);
private:
bool hasAssigned(int * eff);
bool hasZero(int * eff);
void changeMinToMax();
void getRowColZero(int * eff);
void assignAndGetoff(int * eff);
void changeEfficiency(int * eff);
void getTaskAssignment(int * eff);
void assignCore(int * eff);
void resetZero (int * eff);
int getLineNum(int *eff);
private:
bool allAssigned;
int * task;
int * efficiency;
int scale;
int * rowTicked;
int * colTicked;
public: // 定义的几个常数
const int MAXVALUE ;
const bool MAXASSIGN;
const bool MINASSIGN;
const int ASSIGNED ;
const int GETOFFED ;
};
Assignment类说明
1.Assignment类实现任务指派;
2.使用示例
Assignment assignment = Assignment(n, eff);
int * Task = assignment.getTaskAssignment(isMin);
其中,n表示指派任务个数;
eff 效率系数,以一维数组传递;
isMin 是否取最小代价指派;isMin=true时,表示为最小代价指派,缺省情况下,isMin=true;
Task 指派结果,若Task[i]=j, 表示第i个人完成第j项任务(以0为第一序数);
3.应用举例:
int main()
{
int scale, *eff, * task;
int i, j;
// Get Scale
cout<<"\nThe Scale Of Problem: ";
cin>>scale;
// Construct eff
eff = new int[scale*scale];
// Get Efficiency
for(i=0; i<scale; i++)
for(j=0; j<scale; j++)
{
cout<<"\nThe Efficiency Of The "<<i+1<<"th Person Doing The "
<<j+1<<"th Task Is (Natural Number): ";
cin>>eff[i*scale+j];
}
// Get minormax
bool isMin= true;
char isY;
cout<<"\nIs a min cost assignment ?(Y for yes, & others for no): ";
cin>>isY;
isMin= (isY=='Y' || isY=='y');
// List the Efficiency
cout<<"\n" <<"The Effienciency:";
for(i=0; i<scale; i++)
{
cout<<"\nThe " <<i+1 <<"th person: ";
for(j=0; j<scale; j++)
cout<<eff[i*scale+j] <<" ";
}
// Assignment Type
if (isMin)
cout<<"\n\nMin Cost Assignment.\n";
else
cout<<"\n\nMax Effect Assignment.\n";
Assignment assignment = Assignment(scale, (int *)eff);
task = assignment.getAssignment(isMin);
// Output the result
for(i=0; i<scale; i++)
cout<<"\nThe " <<i+1 <<"th person do the " <<task[i]+1 <<"th task;" ;
cout<<"\n";
return 0;
}
按照提示输入问题规模、相应的效率矩阵、求解方式(最大效率或最小代价)后程序就可以输出结果。
以下是assignment.cpp的内容,assignment.h的内容即类的定义部分.
#include "assignment.h"
Assignment::Assignment(int n, int * eff):
MAXVALUE(65532), MAXASSIGN(false), MINASSIGN(true),
ASSIGNED(-1), GETOFFED(-2)
{
scale = n;
efficiency = new int[scale*scale];
for(int i=0; i<scale; i++)
for(int j=0; j<scale; j++)
efficiency[i*scale+j] = eff[i*scale+j];
rowTicked = new int[scale];
colTicked = new int[scale];
task = new int[scale];
}
Assignment::~Assignment()
{
delete [] efficiency;
delete [] rowTicked;
delete [] colTicked;
delete [] task;
}
int * Assignment::getAssignment(bool isMin)
{
if (!isMin)
changeMinToMax();
allAssigned = false;
getRowColZero(efficiency);
assignAndGetoff(efficiency);
assignCore(efficiency);
return task; // Task is set by assignCore()
}
void Assignment::changeMinToMax()
{
int max = 0, i,j;
// Get Max
for(i=0; i<scale; i++)
for(j=0; j<scale; j++)
if (efficiency[i*scale+j] > max)
max = efficiency[i*scale+j];
// Change
for(i=0; i<scale; i++)
for(j=0; j<scale; j++)
efficiency[i*scale+j] -= max;
}
void Assignment::getRowColZero(int * eff)
{
int i,j , min;
// Zero in Row
for(i=0; i<scale; i++)
{
min = MAXVALUE; // Get Min
for(j=0; j<scale; j++)
if (eff[i*scale+j] < min)
min = eff[i*scale+j];
if (min==0) continue;
for(j=0; j<scale; j++)
eff[i*scale+j] -= min;
}
// Zero in Col
for(j=0; j<scale; j++)
{
min = MAXVALUE; // Get Min
for(i=0; i<scale; i++)
if (eff[i*scale+j] < min)
min = eff[i*scale+j];
if (min ==0) continue;
for(i=0; i<scale; i++)
eff[i*scale+j] -= min;
}
}
void Assignment::assignAndGetoff(int * eff)
{
int i,j,k, rowZero, colZero, zeroPos;
bool isOver = false;
while (!isOver)
{
isOver = true;
// From row which has only one zero
for(i=0; i<scale; i++)
{
rowZero = 0;
for(j=0; j<scale; j++)
if (eff[i*scale+j] == 0)
{
rowZero++;
zeroPos = j; // Write down the col number
}
// Only one zero in row
if (rowZero == 1)
{
isOver = false;
eff[i*scale+zeroPos] = ASSIGNED;
for(k=0; k<scale; k++) // Zero in the column maked by GETOFFED
if (eff[k*scale+zeroPos]==0)
eff[k*scale+zeroPos] = GETOFFED;
}
} // End for____Row
// Column process
for(j=0; j<scale; j++)
{
colZero = 0;
for(i=0; i<scale; i++)
if (eff[i*scale+j]==0)
{
colZero++;
zeroPos = i; // Write down the row number
}
if (colZero==1)
{
isOver = true;
eff[zeroPos*scale+j] = ASSIGNED;
for(k=0; k<scale; k++)
if (eff[zeroPos*scale+k]==0)
eff[zeroPos*scale+k] = GETOFFED;
} // End If
} // End for____Column
} // End while
return;
}
/*void Assignment::assignWithStart(int * eff, int row, int col)
{
int i,j;
if (allAssigned)
return;
if (hasZero(eff))
{
for(i=0; i<scale; i++)
{
if (allAssigned)
return;
for(j=0; j<scale; j++)
{
if (allAssigned)
return;
if (eff[i*scale+j]==0)
{
int * tempEff = new int[scale*scale];
int k,m;
for(k=0; k<scale; k++)
for(m=0; m<scale; m++)
tempEff[k*scale+m] = eff[k*scale+m];
tempEff[row*scale+col] = ASSIGNED;
for(k=0; k<scale; k++)
if (tempEff[k*scale+col]==0)
tempEff[k*scale+col] = GETOFFED;
for(k=0; k<scale; k++)
if (tempEff[row*scale+k]==0)
tempEff[row*scale+k] = GETOFFED;
// Get new row, col Start
for(k=0; k<scale; k++)
for(m=0; m<scale; m++)
if (tempEff[k*scale+m]==0)
assignWithStart(tempEff, k, m);
delete [] tempEff;
} // End if
}
}
}
else // No zero
{
while (getLineNum(eff) < scale)
{
changeEfficiency(eff);
if (hasAssigned(eff)) break;
}
if (hasAssigned(eff))
{
allAssigned = true;
getTaskAssignment(eff);
return;
}
} // End else
}*/
int Assignment::getLineNum(int * eff)
{
int i,j, lineNum=0;
bool hasMore = true;
// Initiate rowTicked & colTicked
for(i=0; i<scale; i++)
{
rowTicked[i] = colTicked[i] = 0;
}
// Get rows that has no ASSIGNED
for(i=0; i<scale; i++)
{
rowTicked[i] = 1;
for(j=0; j<scale; j++)
if (eff[i*scale+j]==ASSIGNED)
{
rowTicked[i] = 0;
break;
}
}
int time=0;
while ( (hasMore)&& (time++<scale) )
{
hasMore = false;
// Column
for(i=0; i<scale; i++)
if (rowTicked[i]==1)
for(j=0; j<scale; j++)
if ( (eff[i*scale+j]==ASSIGNED) ||
(eff[i*scale+j]==GETOFFED) )
{
colTicked[j] = 1;
hasMore = true;
break;
}
// Row
for(j=0; j<scale; j++)
if (colTicked[j])
for(i=0; i<scale; i++)
if (eff[i*scale+j]==ASSIGNED)
{
rowTicked[i] = 1;
hasMore = true;
break;
}
} // End while
for(i=0; i<scale; i++)
{
if (rowTicked[i]==0)
lineNum++;
if (colTicked[j]==1)
lineNum++;
}
return lineNum;
}
void Assignment::changeEfficiency(int * eff)
{
int i,j, minValue = MAXVALUE;
// Get minValue in where lines donot cover
for(i=0; i<scale; i++)
{
if (rowTicked[i]==0) continue;
for(j=0; j<scale; j++)
{
if (colTicked[j]==1) continue;
if (eff[i*scale+j] < minValue)
minValue = eff[i*scale+j];
}
}
// To get more zero
for(i=0; i<scale; i++)
if (rowTicked[i]==1)
for(j=0; j<scale; j++)
eff[i*scale+j] -= minValue;
for(j=0; j<scale; j++)
if (colTicked[j]==1)
for(i=0; i<scale; i++)
eff[i*scale+j] += minValue;
}
void Assignment::getTaskAssignment(int * eff)
{
for(int i=0; i<scale; i++)
for(int j=0; j<scale; j++)
if (eff[i*scale+j]==ASSIGNED)
{
task[i] = j; // Person i do the task j
break;
}
}
bool Assignment::hasZero(int * eff)
{
for(int i=0; i<scale; i++)
for(int j=0; j<scale; j++)
if (eff[i*scale+j]==0) return true;
return false;
}
bool Assignment::hasAssigned(int * eff)
{
int i, j;
for(i=0; i<scale; i++)
{
for(j=0; j<scale; j++)
if (eff[i*scale+j]==ASSIGNED)
break;
if (j==scale) return false;
}
return true;
}
void Assignment::assignCore(int * eff)
{
if (hasAssigned(eff))
{
getTaskAssignment(eff);
allAssigned = true;
return;
}
else
{
// Zero not Assigned ?
if (hasZero(eff))
{
// Not All Assigned & has Zero
for(int i=0; i<scale; i++)
{
if (allAssigned) break;
for(int j=0; j<scale; j++)
{
if (allAssigned) break;
// Get the first zero, for getting the best zero is so difficult.
if (eff[i*scale+j]==0)
{
int m;
int * tempEff = new int [scale*scale];
// Keep the copy
for(m=0; m<scale*scale; m++)
tempEff[m] = eff[m];
tempEff[i*scale + j] = ASSIGNED; // Marked as Assigned
for(m=0; m<scale; m++)
{
// Marked Zero in the same col As GETOFFED
if (tempEff[m*scale + j]==0) tempEff[m*scale+j] = GETOFFED;
// Marked Zero in the same row As GETOFFED
if (tempEff[i*scale + m]==0) tempEff[i*scale+m] = GETOFFED;
} // End for___m
//if (! allAssigned)
assignCore(tempEff); // Nested Call
delete [] tempEff;
}
} // End for___j
} // End for___i(To get rid of Dependent Zero
} // End if____(hasZero(eff))
// Not All Assigned & has No Zero
else
{
if (getLineNum(eff)<scale) // l<n
{
changeEfficiency(eff); // Change Efficiency Matrix
resetZero(eff);
getRowColZero(eff);
assignAndGetoff(eff);
assignCore(eff);
}
else // l=n
return;
}
} // End else
return;
}
void Assignment::resetZero(int * eff)
{
for(int i=0; i<scale*scale; i++)
if ((eff[i]==ASSIGNED)||(eff[i]==GETOFFED))
eff[i]=0;
}