运筹学指派问题的匈牙利法实验报告

合集下载

指派问题的匈牙利法

指派问题的匈牙利法
(5)若◎ 元素的数目m 等于矩阵的阶数n,那么这指 派问题的最优解已得到。若m < n, 则转入下一步。
第三步:作最少的直线覆盖所有0元素。 (1)对没有◎的行打√号; (2)对已打√号的行中所有含Ø元素的列打√号; (3)再对打有√号的列中含◎ 元素的行打√号;
(4)重复(2),(3)直到得不出新的打√号的行、列为止; (5)对没有打√号的行画横线,有打√号的列画纵线, 这就得到覆盖所有0元素的最少直线数 l 。l 应等于m, 若不相等,说明试指派过程有误,回到第二步(4),另 行试指派;若 l=m < n,须再变换当前的系数矩阵, 以找到n个独立的0元素,为此转第四步。

l =m=4 < n=5
2 ◎0 4 2 4 2 5 Ø0 3 ◎0 4 1 ◎0 1 3 4 Ø0 3 5 1 ◎0 2 3 0Ø 5
1 0 3 1 3 2 6 0 3 0 4 2 0 1 3 3 0 2 4 0 0 3 3 0 5
1 0 3 1 3 2 6 0 3 0 4 2 0 1 3 3 0 2 4 0 0 3 3 0 5
0Ø ◎0 3 0Ø 3 1 6 0◎ 2 Ø0 3 2 0Ø 0◎ 3 2 Ø0 2 3 ◎0 ◎0 4 4 0Ø 6
28
此问题有多个最优解
0◎ 0Ø 3 0Ø 3 1 6 0Ø 2 ◎0 3 2 0◎ 0Ø 3 2 ◎0 2 3 0Ø 0Ø 4 4 0◎ 6
Ø0 0Ø 3 ◎0 3 1 6 0Ø 2 ◎0 3 2 0◎ 0Ø 3 2 ◎0 2 3 0Ø 0◎ 4 4 0Ø 6
15 14 6 6 10
4 10 7 10 9
减去最小元素
5 0 2 0 2
2
3
0
0

指派问题匈牙利算法最大值

指派问题匈牙利算法最大值

指派问题匈牙利算法最大值
指派问题是一个优化问题,旨在确定如何将 n 个任务分配给 n 个人员,以便完成总成本最小或总利润最大。

匈牙利算法是解决指派问题的经典算法之一,通过寻找增广路径来找到最大权值的匹配。

在指派问题中,我们有一个 n x n 的成本矩阵,其中的每个元素表
示将特定任务分配给特定人员的成本或利润。

问题的目标是找到一种分配方式,使得总成本最小或总利润最大。

匈牙利算法是一种基于图论的算法,它通过构建二分图和寻找增广路径来解决指派问题。

算法的核心思想是通过不断改进当前的匹配,直到找到最优解。

具体来说,匈牙利算法的步骤如下:
1. 初始化一个空的匹配集合。

2. 对于每个任务,找到一个未被分配的人员,并将其分配给该任务。

如果该任务没有未被分配的人员,则考虑将其他任务分配给当前人员,并将当前任务分配给其它人员。

3. 如果存在一个未被匹配的任务,寻找一条从该任务出发的增广路径。

增广路径是一条交替经过匹配边和非匹配边的路径,起点和终点都是未匹配的任务。

4. 如果存在增广路径,则改进当前的匹配,即通过将增广路径上的
非匹配边变为匹配边,并将增广路径上的匹配边变为非匹配边。

5. 重复步骤3和步骤4,直到不存在增广路径为止。

匈牙利算法的运行时间复杂度为 O(n^3),其中 n 是任务或人员的数量。

该算法可以找到指派问题的最优解,并且在实践中表现良好。

总之,指派问题是一个重要的优化问题,而匈牙利算法是一种解决指派问题的经典算法。

通过构建二分图并寻找增广路径,匈牙利算法可以找到指派问题的最优解。

基于匈牙利算法的物资运输的车辆分派问题研究

基于匈牙利算法的物资运输的车辆分派问题研究

基于匈牙利算法的物资运输的车辆分派问题研究摘要:针对物资运输的车辆分派问题本文利用匈牙利算法,从使得运输总费用最小的角度出发,构建了运输车辆模型及车辆分派模型,以求得最佳的车辆运输分派方案。

关键词:匈牙利算法;运输车辆分派;优化一、武警部队物资运输车辆分派问题的由来运输武警部队后勤部的车辆运输成本是年度财政计划的重要部分,其合理的开支也是关系部队科学发展、高效转型的关键因素。

然而在实际的运输过程中,武警部队运输成本居高不低,这除了国内形势日趋复杂、运输频率和数量增加的原因外,还与指挥人员合理安排运输车辆,采取最优的运输分配方案息息相关。

要想降低车辆运输的基本成本必须从运输方式的选择、运输路线的规划、车辆任务的分派三个方面考虑。

二、分配问题及匈牙利法的相关理论研究运筹学中的分配问题,或称指派问题(AignmentProblem),是一种特殊的整数规划问题,在许多应用领域中会经常遇到,例如:有N项任务,分配给N个人完成,并指定每人完成其中的一项,每项任务只交给一个人去完成,应如何分配,使得费用最低。

这是经典分配问题的一个实例,问题中的任务可能是任何类型的活动,人可能是任何类型的资源,费用可能是任何类型的效能。

分配问题最简单的处理方法,就是进行所有可能的分配,共有N的全排列个(N!)分配方案,再从中选出最优解,但当N较大时,分配数将产生组合爆炸,当今的高速计算机也都无法处理。

分配问题也是个线型规划问题,若用正规的单纯形法,或借助于运输问题特点的一些特殊方法,也可以用来解决这个问题,但效果不好。

而一种称为“匈牙利法”的分配算法,是目前被认为是用来解决分配问题的最有效算法,“运筹学”和“最优化技术”等专著,都将它作为标准的算法进行介绍。

“匈牙利法”的计算和前提是:从效能矩阵的每一行(每一列)元素中分别减去(或加上)一个常数,使得每一行(每一列)里至少有一个0元素,得到新的效能矩阵,用它来取代原效能矩阵,将不改变分配问题的最优解。

求解指派问题的匈牙利算法.doc

求解指派问题的匈牙利算法.doc

3.2 求解指派问题的匈牙利算法由于指派问题的特殊性,又存在着由匈牙利数学家D.Konig 提出的更为简便的解法—匈牙利算法。

算法主要依据以下事实:如果系数矩阵)(ij c C =一行(或一列)中每一元素都加上或减去同一个数,得到一个新矩阵)(ij b B = ,则以C 或B 为系数矩阵的指派问题具有相同的最优指派。

利用上述性质,可将原系数阵C 变换为含零元素较多的新系数阵B ,而最优解不变。

若能在B 中找出n 个位于不同行不同列的零元素,令解矩阵中相应位置的元素取值为1,其它元素取值为零,则所得该解是以B 为系数阵的指派问题的最优解,从而也是原问题的最优解。

由C 到B 的转换可通过先让矩阵C 的每行元素均减去其所在行的最小元素得矩阵D ,D 的每列元素再减去其所在列的最小元素得以实现。

下面通过一例子来说明该算法。

例7 求解指派问题,其系数矩阵为⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎣⎡=16221917171822241819211722191516C 解 将第一行元素减去此行中的最小元素15,同样,第二行元素减去17,第三行元素减去17,最后一行的元素减去16,得⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎣⎡=06310157124074011B 再将第3列元素各减去1,得⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎣⎡=****20531005711407301B 以2B 为系数矩阵的指派问题有最优指派⎪⎪⎭⎫ ⎝⎛43124321 由等价性,它也是例7的最优指派。

有时问题会稍复杂一些。

例8 求解系数矩阵C 的指派问题⎥⎥⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎢⎢⎣⎡=61071041066141512141217766698979712C 解:先作等价变换如下∨∨∨⎥⎥⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎢⎢⎣⎡→⎥⎥⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎢⎢⎣⎡----- 2636040*08957510*00*0032202*056107104106614151214121776669897971246767 容易看出,从变换后的矩阵中只能选出四个位于不同行不同列的零元素,但5=n ,最优指派还无法看出。

指派问题的匈牙利法

指派问题的匈牙利法

4 0 2 3
5 9 0 1 5 4 0 9 3 7 6 0
4 0 2 3
5 4 0 1 0 4 0 4 3 7 1 0
第二步,试指派: 第二步,试指派:
-5
举例说明 1)表上作业法 2)匈牙利法
例 有四个工人和四台不同的机床,每位工人在不 同的机床上完成给定的任务的工时如表5.12所示, 问安排哪位工人操作哪一台机床可使总工时最少?
任务1 工人1 工人2 工人3 工人4 2 15 13 4
任务2 10 4 14 7
任务3 3 14 16 13
任务4 7 8 11 9
0 0 1 0
0 0 1 1 0 0 0 0 0 0 1 0
例二、 例二、 有一份中文说明书,需译成英、 有一份中文说明书,需译成英、日、德、俄四种
文字,分别记作A、 、 、 。现有甲、 文字,分别记作 、B、C、D。现有甲、乙、丙、丁四 人,他们将中文说明书译成不同语种的说明书所需时 间如下表所示,问如何分派任务,可使总时间最少? 间如下表所示,问如何分派任务,可使总时间最少?
再看一例
请求解如下矩阵表达的指派问题
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
Байду номын сангаас
减去最小元素
5 0 2 0 2 2 3 0 0 0 0 10 5 7 2 9 8 0 0 10 0 6 3 6 5

调整可行解
7 4 0 11 0
0 2 0 3 0 0 8 3 5 8 0 0 4 1 4
2 0 0 10 3

匈牙利算法求解教学任务指派问题

匈牙利算法求解教学任务指派问题

Xij
(1)
使得总效益最高(时间最少、成本最小、收益最大等),
即目标函数
。当

时, 为
一对一指派问题;否则为多人协作或兼职问题。 求解指派问题的方法通常有分支定界法、隐枚举法、
匈 牙 利 法 等 [1]。 匈 牙 利 算 法 由 匈 牙 利 数 学 家 Edmonds 于 1965 年提出,是基于 Hall 定理中充分性证明的思想,用增 广路径求二分图最大匹配的算法,算法的核心是寻找增广 路径,也可用于指派问题的求解 [2]。
指派问题的数学模型通常是:设 n 个人(或机器)被 分配去做 m 件工作,由于工作性质和各人(或机器)的专 长不同,完成不同工作的效益(时间、成本、收益等)将有 差别,用系数矩阵 C 表示,Cij 表示第 i 个人完成第 j 件工作 的效益,Cij ≥ 0(i=1,...,n;j=1,...,m)。当 n=m 时,为 平衡状态下的标准指派问题;当 n > m 时,人数多于任务数, 属于不平衡状态下择优录用问题;当 n < m 时,人数少于 任务数,可以分为某些任务不管和一人完成多项任务两种 情况,属于不平衡状态下指派问题的拓展。求解指派矩阵 X:
针对多人执行多项工作的指派问题,张云华采用匈牙 利算法的基本思想和步骤进行了研究 [3]。目标分配问题作 为指派问题的一种类型,谷稳综合匈牙利算法及其进化算 法的特点,对机器人足球的目标分配问题进行了研究 [4]。 为避免匈牙利算法多次试分配导致处理速度慢的不足,周 莉等人对寻找独立零的次序进行改进,得到匈牙利算法求 解指派问题的一次性分配算法 [5]。李延鹏等人提出利用虚 拟工作代替并联环境,将具有并联环节的人员指派问题转
本文基于匈牙利算法,建立教学任务指派优化模型, 分析如何分配教师承担教学任务以使系统整体现求解。

运筹学指派问题的匈牙利法

运筹学指派问题的匈牙利法

运筹学课程设计指派问题的匈牙利法专业:姓名:学号:1.算法思想:匈牙利算法的基本思想是修改效益矩阵的行或列,使得每一行或列中至少有一个为零的元素,经过修正后,直至在不同行、不同列中至少有一个零元素,从而得到与这些零元素相对应的一个完全分配方案。

当它用于效益矩阵时,这个完全分配方案就是一个最优分配,它使总的效益为最小。

这种方法总是在有限步內收敛于一个最优解。

该方法的理论基础是:在效益矩阵的任何行或列中,加上或减去一个常数后不会改变最优分配。

2.算法流程或步骤:1.将原始效益矩阵C的每行、每列各元素都依次减去该行、该列的最小元素,使每行、每列都至少出现一个0元素,以构成等价的效益矩阵C’。

2.圈0元素。

在C’中未被直线通过的含0元素最少的行(或列)中圈出一个0元素,通过这个0元素作一条竖(或横)线。

重复此步,若这样能圈出不同行不同列的n个0元素,转第四步,否则转第三步。

3.调整效益矩阵。

在C’中未被直线穿过的数集D中,找出最小的数d,D中所有数都减去d,C’中两条直线相交处的数都加的d。

去掉直线,组成新的等价效益矩阵仍叫C’,返回第二步。

X=0,这就是一种最优分配。

最低总4.令被圈0元素对应位置的X ij=1,其余ij耗费是C中使X=1的各位置上各元素的和。

ij算法流程图:3.算法源程序:#include<iostream.h>typedef struct matrix{float cost[101][101];int zeroelem[101][101];float costforout[101][101];int matrixsize;int personnumber;int jobnumber;}matrix;matrix sb;int result[501][2];void twozero(matrix &sb);void judge(matrix &sb,int result[501][2]);void refresh(matrix &sb);void circlezero(matrix &sb);matrix input();void output(int result[501][2],matrix sb);void zeroout(matrix &sb);matrix input(){matrix sb;int m;int pnumber,jnumber;int i,j;float k;char w;cout<<"指派问题的匈牙利解法:"<<endl;cout<<"求最大值,请输入1;求最小值,请输入0:"<<endl;cin>>m;while(m!=1&&m!=0){cout<<"请输入1或0:"<<endl;cin>>m;}cout<<"请输入人数(人数介于1和100之间):"<<endl;cin>>pnumber;while(pnumber<1||pnumber>100){cout<<"请输入合法数据:"<<endl;cin>>pnumber;}cout<<"请输入工作数(介于1和100之间):"<<endl;cin>>jnumber;while(jnumber<1||jnumber>100){cout<<"请输入合法数据:"<<endl;cin>>jnumber;}cout<<"请输入"<<pnumber<<"行"<<jnumber<<"列的矩阵,同一行内以空格间隔,不同行间以回车分隔,以$结束输入:\n";for(i=1;i<=pnumber;i++)for(j=1;j<=jnumber;j++){cin>>sb.cost[i][j];sb.costforout[i][j]=sb.cost[i][j];}cin>>w;if(jnumber>pnumber)for(i=pnumber+1;i<=jnumber;i++)for(j=1;j<=jnumber;j++){sb.cost[i][j]=0;sb.costforout[i][j]=0;}else{if(pnumber>jnumber)for(i=1;i<=pnumber;i++)for(j=jnumber+1;j<=pnumber;j++){sb.cost[i][j]=0;sb.costforout[i][j]=0;}}sb.matrixsize=pnumber;if(pnumber<jnumber)sb.matrixsize=jnumber;sb.personnumber=pnumber;sb.jobnumber=jnumber;if(m==1){k=0;for(i=1;i<=sb.matrixsize;i++)for(j=1;j<=sb.matrixsize;j++)if(sb.cost[i][j]>k)k=sb.cost[i][j];for(i=1;i<=sb.matrixsize;i++)for(j=1;j<=sb.matrixsize;j++)sb.cost[i][j]=k-sb.cost[i][j];}return sb;}void circlezero(matrix &sb){int i,j;float k;int p;for(i=0;i<=sb.matrixsize;i++)sb.cost[i][0]=0;for(j=1;j<=sb.matrixsize;j++)sb.cost[0][j]=0;for(i=1;i<=sb.matrixsize;i++)for(j=1;j<=sb.matrixsize;j++)if(sb.cost[i][j]==0){sb.cost[i][0]++;sb.cost[0][j]++;sb.cost[0][0]++;}for(i=0;i<=sb.matrixsize;i++)for(j=0;j<=sb.matrixsize;j++)sb.zeroelem[i][j]=0;k=sb.cost[0][0]+1;while(sb.cost[0][0]<k){k=sb.cost[0][0];for(i=1;i<=sb.matrixsize;i++){if(sb.cost[i][0]==1){for(j=1;j<=sb.matrixsize;j++)if(sb.cost[i][j]==0&&sb.zeroelem[i][j]==0)break;sb.zeroelem[i][j]=1;sb.cost[i][0]--;sb.cost[0][j]--;sb.cost[0][0]--;if(sb.cost[0][j]>0)for(p=1;p<=sb.matrixsize;p++)if(sb.cost[p][j]==0&&sb.zeroelem[p][j]==0){sb.zeroelem[p][j]=2;sb.cost[p][0]--;sb.cost[0][j]--;sb.cost[0][0]--;}}}for(j=1;j<=sb.matrixsize;j++){if(sb.cost[0][j]==1){for(i=1;i<=sb.matrixsize;i++)if(sb.cost[i][j]==0&&sb.zeroelem[i][j]==0)break;sb.zeroelem[i][j]=1;sb.cost[i][0]--;sb.cost[0][j]--;sb.cost[0][0]--;if(sb.cost[i][0]>0)for(p=1;p<=sb.matrixsize;p++)if(sb.cost[i][p]==0&&sb.zeroelem[i][p]==0){sb.zeroelem[i][p]=2;sb.cost[i][0]--;sb.cost[0][p]--;sb.cost[0][0]--;}}}}if(sb.cost[0][0]>0)twozero(sb);elsejudge(sb,result);}void twozero(matrix &sb){int i,j;int p,q;int m,n;float k;matrix st;for(i=1;i<=sb.matrixsize;i++)if(sb.cost[i][0]>0)break;if(i<=sb.matrixsize){for(j=1;j<=sb.matrixsize;j++){st=sb;if(sb.cost[i][j]==0&&sb.zeroelem[i][j]==0){sb.zeroelem[i][j]=1;sb.cost[i][0]--;sb.cost[0][j]--;sb.cost[0][0]--;for(q=1;q<=sb.matrixsize;q++)if(sb.cost[i][q]==0&&sb.zeroelem[i][q]==0){sb.zeroelem[i][q]=2;sb.cost[i][0]--;sb.cost[0][q]--;sb.cost[0][0]--;}for(p=1;p<=sb.matrixsize;p++)if(sb.cost[p][j]==0&&sb.zeroelem[p][j]==0){sb.zeroelem[p][j]=2;sb.cost[p][0]--;sb.cost[0][j]--;sb.cost[0][0]--;}k=sb.cost[0][0]+1;while(sb.cost[0][0]<k){k=sb.cost[0][0];for(p=i+1;p<=sb.matrixsize;p++){if(sb.cost[p][0]==1){for(q=1;q<=sb.matrixsize;q++)if(sb.cost[p][q]==0&&sb.zeroelem[p][q]==0)break;sb.zeroelem[p][q]=1;sb.cost[p][0]--;sb.cost[0][q]--;sb.cost[0][0]--;for(m=1;m<=sb.matrixsize;m++)if(sb.cost[m][q]=0&&sb.zeroelem[m][q]==0){sb.zeroelem[m][q]=2;sb.cost[m][0]--;sb.cost[0][q]--;sb.cost[0][0]--;}}}for(q=1;q<=sb.matrixsize;q++){if(sb.cost[0][q]==1){for(p=1;p<=sb.matrixsize;p++)if(sb.cost[p][q]==0&&sb.zeroelem[p][q]==0)break;sb.zeroelem[p][q]=1;sb.cost[p][q]--;sb.cost[0][q]--;sb.cost[0][0]--;for(n=1;n<=sb.matrixsize;n++)if(sb.cost[p][n]==0&&sb.zeroelem[p][n]==0){sb.zeroelem[p][n]=2;sb.cost[p][0]--;sb.cost[0][n]--;sb.cost[0][0]--;}}}}if(sb.cost[0][0]>0)twozero(sb);elsejudge(sb,result);}sb=st;}}}void judge(matrix &sb,int result[501][2]){int i,j;int m;int n;int k;m=0;for(i=1;i<=sb.matrixsize;i++)for(j=1;j<=sb.matrixsize;j++)if(sb.zeroelem[i][j]==1)m++;if(m==sb.matrixsize){k=1;for(n=1;n<=result[0][0];n++){for(i=1;i<=sb.matrixsize;i++){for(j=1;j<=sb.matrixsize;j++)if(sb.zeroelem[i][j]==1)break;if(i<=sb.personnumber&&j<=sb.jobnumber)if(j!=result[k][1])break;k++;}if(i==sb.matrixsize+1)break;elsek=n*sb.matrixsize+1;}if(n>result[0][0]){k=result[0][0]*sb.matrixsize+1;for(i=1;i<=sb.matrixsize;i++)for(j=1;j<=sb.matrixsize;j++)if(sb.zeroelem[i][j]==1){result[k][0]=i;result[k++][1]=j;}result[0][0]++;}}else{refresh(sb);}}void refresh(matrix &sb){int i,j;float k;int p;k=0;for(i=1;i<=sb.matrixsize;i++){for(j=1;j<=sb.matrixsize;j++)if(sb.zeroelem[i][j]==1){sb.zeroelem[i][0]=1;break;}}while(k==0){k=1;for(i=1;i<=sb.matrixsize;i++)if(sb.zeroelem[i][0]==0){sb.zeroelem[i][0]=2;for(j=1;j<=sb.matrixsize;j++)if(sb.zeroelem[i][j]==2){sb.zeroelem[0][j]=1;}}for(j=1;j<=sb.matrixsize;j++){if(sb.zeroelem[0][j]==1){sb.zeroelem[0][j]=2;for(i=1;i<=sb.matrixsize;i++)if(sb.zeroelem[i][j]==1){sb.zeroelem[i][0]=0;k=0;}}}}p=0;k=0;for(i=1;i<=sb.matrixsize;i++){if(sb.zeroelem[i][0]==2){for(j=1;j<=sb.matrixsize;j++){if(sb.zeroelem[0][j]!=2)if(p==0){k=sb.cost[i][j];p=1;}else{if(sb.cost[i][j]<k)k=sb.cost[i][j];}}}}for(i=1;i<=sb.matrixsize;i++){if(sb.zeroelem[i][0]==2)for(j=1;j<=sb.matrixsize;j++)sb.cost[i][j]=sb.cost[i][j]-k;}for(j=1;j<=sb.matrixsize;j++){if(sb.zeroelem[0][j]==2)for(i=1;i<=sb.matrixsize;i++)sb.cost[i][j]=sb.cost[i][j]+k;}for(i=0;i<=sb.matrixsize;i++)for(j=0;j<=sb.matrixsize;j++)sb.zeroelem[i][j]=0;circlezero(sb);}void zeroout(matrix &sb){int i,j;float k;for(i=1;i<=sb.matrixsize;i++){k=sb.cost[i][1];for(j=2;j<=sb.matrixsize;j++)if(sb.cost[i][j]<k)k=sb.cost[i][j];for(j=1;j<=sb.matrixsize;j++)sb.cost[i][j]=sb.cost[i][j]-k;}for(j=1;j<=sb.matrixsize;j++){k=sb.cost[1][j];for(i=2;i<=sb.matrixsize;i++)if(sb.cost[i][j]<k)k=sb.cost[i][j];for(i=1;i<=sb.matrixsize;i++)sb.cost[i][j]=sb.cost[i][j]-k;}}void output(int result[501][2],matrix sb) {int k;int i;int j;int p;char w;float v;v=0;for(i=1;i<=sb.matrixsize;i++){v=v+sb.costforout[i][result[i][1]];}cout<<"最优解的目标函数值为"<<v;k=result[0][0];if(k>5){cout<<"解的个数超过了限制."<<endl;k=5;}for(i=1;i<=k;i++){cout<<"输入任意字符后输出第"<<i<<"种解."<<endl;cin>>w;p=(i-1)*sb.matrixsize+1;for(j=p;j<p+sb.matrixsize;j++)if(result[j][0]<=sb.personnumber&&result[j][1]<=sb.jobnumber)cout<<"第"<<result[j][0]<<"个人做第"<<result[j][1]<<"件工作."<<endl;}}void main(){result[0][0]=0;sb=input();zeroout(sb);circlezero(sb);output(result,sb);}4. 算例和结果:自己运算结果为:->⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎣⎡3302102512010321->⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎣⎡330110241200032034526635546967562543----⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎣⎡可以看出:第1人做第4件工作;第2人做第1件工作;第3人做第3件工作;第4人做第2件工作。

指派问题的匈牙利解法

指派问题的匈牙利解法

指派问题的匈牙利解法1、 把各行元素分别减去本行元素的最小值;然后在此基础上再把每列元素减去本列中的最小值。

⎪⎪⎪⎪⎪⎪⎭⎫ ⎝⎛⇒⎪⎪⎪⎪⎪⎪⎭⎫ ⎝⎛0 4 3 2 04 0 5 0 01 2 3 2 03 7 7 1 08 11 0 3 06 10 12 9 610 6 14 7 67 8 12 9 61014 17 9 712 15 7 8 4 此时每行及每列中肯定都有0元素了。

2、 确定独立零元素,并作标记。

(1)、首先逐行判断是否有含有独立0元素的行,如果有,则按行继续处理;如没有,则要逐列判断是否有含有独立0元素的列,若有,则按列继续处理。

若既没有含有独立0元素的行,也没有含有独立0元素的列,则仍然按行继续处理。

(2)在按行处理时,若某行有独立0元素,把该0元素标记为a ,把该0所在的列中的其余0元素标记为b ;否则,暂时越过本行,处理后面的行。

把所有含有独立0元素的行处理完毕后,再回来处理含有2个以及2个以上的0元素的行:任选一个0做a 标记,再把该0所在行中的其余0元素及所在列中的其余0元素都标记为b 。

(3)在按列处理时,若某列有独立0元素,把该0元素标记为a ,把该0所在的行中的其余0元素标记为b ;否则,暂时越过本列,处理后面的列。

把所有含有独立0元素的列处理完毕后,再回来处理含有2个以及2个以上的0元素的列:任选一个0做a 标记,再把该0所在列中的其余0元素及所在行中的其余0元素都标记为b 。

(4)、重复上述过程,即得到独立零元素(标记a 的“0”)⎪⎪⎪⎪⎪⎪⎭⎫ ⎝⎛a b b a b b a 04 3 2 04 05 0 01 2 3 2 037 7 1 08 11 0 3 0a b 3、 若独立零元素等于矩阵阶数,则已经得到最优解,若小于矩阵阶数,则继续以下步骤:(1)、对没有标记a 的行作标记c(2)、在已作标记c 的行中,对标记b 所在列作标记c(3)、在已作标记c 的列中,对标记a 所在的行作标记c(4)、对没有标记c 的行划线,对有标记c 的列划线4、 在未被直线覆盖的所有元素中找出一个最小元素(Xmin ),未被直线覆盖的行(或列)中所有元素都减去这个数。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

运筹学课程设计报告专业:班级:学号::2012年6月20日目录一、题目。

二、算法思想。

三、算法步骤。

四、算法源程序。

五、算例和结果。

六、结论与总结。

一、题目:匈牙利法求解指派问题。

二、算法思想。

匈牙利解法的指派问题最优解的以下性质:设指派问题的系数矩阵为C=()c ij n n⨯,若将C的一行(或列)各元素分别减去一个常数k(如该行或列的最小元素),则得到一个新的矩阵C’=()'c ij n n⨯。

那么,以C’位系数矩阵的指派问题和以C位系数矩阵的原指派问题有相同最优解。

由于系数矩阵的这种变化不影响约束方程组,只是使目标函数值减少了常数k,所以,最优解并不改变。

必须指出,虽然不比要求指派问题系数矩阵中无负元素,但在匈牙利法求解指派问题时,为了从以变换后的系数矩阵中判别能否得到最优指派方案,要求此时的系数矩阵中无负元素。

因为只有这样,才能从总费用为零这一特征判定此时的指派方案为最优指派方案。

三、算法步骤。

(1)变换系数矩阵,使各行和各列皆出现零元素。

各行及各列分别减去本行及本列最小元素,这样可保证每行及每列中都有零元素,同时,也避免了出现负元素。

(2)做能覆盖所有零元素的最少数目的直线集合。

因此,若直线数等于n,则以可得出最优解。

否则,转第(3)步。

对于系数矩阵非负的指派问题来说,总费用为零的指派方案一定是最优指派方案。

在第(1)步的基础上,若能找到n个不同行、不同列的零元素,则对应的指派方案总费用为零,从而是最优的。

当同一行(或列)上有几个零元素时,如选择其一,则其与的零元素就不能再被选择,从而成为多余的。

因此,重要的是零元素能恰当地分布在不同行和不同列上,而并在与它们的多少。

但第(1)步并不能保证这一要求。

若覆盖所有零元素的最少数目的直线集合中的直线数目是n,则表明能做到这一点。

此时,可以从零元素的最少的行或列开始圈“0”,每圈一个“0”,同时把位于同行合同列的其他零元素划去(标记为),如此逐步进行,最终可得n个位于不同行、不同列的零元素,他们就对应了最优解;若覆盖所有零元素的最少数目的直线集合中的元素个数少于n,则表明无法实现这一点。

需要对零元素的分布做适当调整,这就是第(3)步。

(3)变换系数矩阵,是未被直线覆盖的元素中出现零元素。

回到第(2)步。

在未被直线覆盖的元素中总有一个最小元素。

对未被直线覆盖的元素所在的行(或列)中各元素都减去这一最小元素,这样,在未被直线覆盖的元素中势必会出现零元素,但同时却又是以被直线覆盖的元素中出现负元素。

为了消除负元素,只要对它们所在的列(或行)中个元素都加上这一最小元素(可以看作减去这一最小元素的相反数)即可。

四、算法源程序。

#include<iostream.h>#include<stdlib.h>#define m 5int input(int M[m][m]){int i,j;for(i=0;i<m;i++){ cout<<"请输入系数矩阵第"<<i+1<<"行元素:"<<endl;for(j=0;j<m;j++)cin>>M[i][j];}cout<<"系数矩阵为:"<<endl;for(i=0;i<m;i++){ for(j=0;j<m;j++)cout<<M[i][j]<<"\t";cout<<endl;}return M[m][m];}int convert(int M[m][m]){ int x[m],y[m];int i,j;for(i=0;i<m;i++){ x[i]=M[i][0];for(j=1;j<m;j++){ if(M[i][j]<x[i])x[i]=M[i][j];}}for(i=0;i<m;i++)for(j=0;j<m;j++)M[i][j]-=x[i];for(j=0;j<m;j++){ y[j]=M[0][j];for(i=0;i<m;i++){ if(M[i][j]<y[j])y[j]=M[i][j];}}for(i=0;i<m;i++)for(j=0;j<m;j++)M[i][j]-=y[j];cout<<"对系数矩阵各行各列进行变换得:"<<endl;for(i=0;i<m;i++){ for(j=0;j<m;j++)cout<<M[i][j]<<"\t";cout<<endl;}return M[m][m];}int exchange(int M[m][m]){ int i,j,n;cout<<"进行行变换输入0,进行列变换输入其他任意键:"<<endl;cin>>n;if(n==0)cout<<"分别输入要变换的行及该行未覆盖元素中最小元素:"<<endl;elsecout<<"分别输入要变换的列及该列的最小元素:"<<endl;int a,b;cin>>a>>b;for(j=0;j<m;j++)if(n==0)M[a-1][j]-=b;elseM[j][a-1]-=b;cout<<"变换后的矩阵:"<<endl;for(i=0;i<m;i++){ for(j=0;j<m;j++)cout<<M[i][j]<<"\t";cout<<endl;}return M[m][m];}void main(){ int M[m][m];cout<<"<<<<<<<<<<<<<<<<<<<匈牙利法解指派问题>>>>>>>>>>>>>>>>>>>> "<<endl;while(true){cout<<""<<endl;cout<<">>>>>>>>>>>>>>>> 菜单 <<<<<<<<<<<<<<<<"<<endl<<endl;cout<<" 1.系数矩阵输入 "<<endl;cout<<" 2.初始变换 "<<endl;cout<<" 3.行列变换 "<<endl;cout<<" 4.退出 "<<endl;cout<<"************************************"<<endl<<endl;int n;cout<<"请选择功能键";cin>>n;switch(n){case 1:input(M);break;case 2:convert(M);break;case 3:exchange(M);break;case 4:cout<<"使用!"<<endl;exit(0);break;default:cout<<"输入有误!请重新输入!"<<endl;}}}五、算例和结果。

例4—12 今有甲、乙、丙、丁4个人去完成5项任务。

每人完成各项任务所需的时间如表4—11所示。

由于任务数多于人数,故规定其中有一人可兼完成两项任务,其余三人各完成一项任务。

是确定总花费时间为最小的指派方案。

(课本P113)假设第5个人是戊,他完成各项任务的时间取甲、乙、丙、丁中的最小者,构造表4—12。

由表4—12可得到系数矩阵C⎥⎥⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎢⎢⎣⎡=32202627244523364224324028273433202638393741312925C1、将系数矩阵C 输入程序。

2、对系数矩阵各行各列减去最小元素,即程序中的初始变换。

3、进行行变换4、进行列变换6、再次进行行变换7、再次进行列变换此时,已经不能用少于五根直线覆盖零元素,故此时为最优指派方案。

⎥⎥⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎢⎢⎣⎡=32202627244523364224324028273433202638393741312925C [][][][][]⎥⎥⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎢⎢⎣⎡→20023120714001800113001318317100最优指派方案是:甲做B ,乙做C ,丙做E ,丁做A ,戊做D ,由于戊(虚拟的人)完成D 的时间与乙相同,实际上最优指派方案是:乙完成C 和D ,甲、丙、丁分别完成B ,E ,A ,总计时间为131。

六、结论和总结。

匈牙利法解指派问题,其步骤简单易懂,操作起来也不难,可是由于计算量实在太大很容易出错,故利用程序来完成对系数矩阵的化简变换是再好不过的。

只要确定输入,以及找出的覆盖集合无误,则计算结果就不会出错。

由于时间仓促,我编的程序还有许多不足之处,比如说:在输入系数矩阵之前,需要事先定义二维数组的行及列。

针对这个问题我尝试了好几次,也没有解决。

查了一些资料,好像可以通过动态分配二维数组的空间大小来实现二维数组行和列的输入。

本来我想实现程序对系数矩阵零元素的直线覆盖功能的,可操作起来实在太难,故改为手动操作。

通过这次课程设计,我不仅对运筹学的知识进行了巩固,也发觉到了编程对数学的强大辅助功能。

利用它可以解决好多数学问题,大大节省了时间及精力。

学好数学和计算机是今后就业的重要筹码。

相关文档
最新文档