本文转载自: https://www.cnblogs.com/chenyg32/p/3293247.html 作者:chenyg32 转载请注明该声明。

指派问题概述:

实际中,会遇到这样的问题,有n项不同的任务,需要n个人分别完成其中的1项,每个人完成任务的时间不一样。于是就有一个问题,如何分配任务使得花费时间最少。

通俗来讲,就是n*n矩阵中,选取n个元素,每行每列各有1个元素,使得和最小。

如下图:

指派问题性质:

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

匈牙利法:

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

1.行归约:

每行元素减去该行的最小元素

5

0

2

0

2

2

3

0

0

0

0

10

5

7

2

9

8

0

0

4

0

6

3

6

5

2.列归约:

每列元素减去该列的最小元素

5

0

2

0

2

2

3

0

0

0

0

10

5

7

2

9

8

0

0

4

0

6

3

6

5

3.试指派:

(1)找到未被画线的含0元素最少的行列,即,遍历所有未被画线的0元素,看下该0元素所在的行列一共有多少个0,最终选取最少个数的那个0元素。

(2)找到该行列中未被画线的0元素,这就是一个独立0元素。对该0元素所在行和列画线。

5

0

2

0

2

2

3

0

0

0

0

10

5

7

2

9

8

0

0

4

0

6

3

6

5

5

0

2

0

2

2

3

0

0

0

0

10

5

7

2

9

8

0

0

4

0

6

3

6

5

5

0

2

0

2

2

3

0

0

0

0

10

5

7

2

9

8

0

0

4

0

6

3

6

5

5

0

2

0

2

2

3

0

0

0

0

10

5

7

2

9

8

0

0

4

0

6

3

6

5

(3)暂时不看被线覆盖的元素,重复(1)(2)直到没有线可以画。

(4)根据(2)找到的0元素个数判断,找到n个独立0元素则Success,小于n个则Fail.(本例子中,n=5,可以看到,第一次试指派之后,独立0元素有4个,不符合)

4.画盖0线:

目标:做最少的直线数覆盖所有0元素,直线数就是独立0元素的个数。

注意:这跟3的线不同;不能用贪心法去画线,比如

1 0 0

1 1 0

1 0 1

若先画横的,则得画3条线,实际只需2条;若先画竖的,将矩阵转置后同理。

步骤3得出的独立0元素的位置

5

0

2

0

2

2

3

0

0

0

0

10

5

7

2

9

8

0

0

4

0

6

3

6

5

(1)对没有独立0元素的行打勾、

(2)对打勾的行所含0元素的列打勾

(3)对所有打勾的列中所含独立0元素的行打勾

(4)重复(2)(3)直到没有不能再打勾

(5)对打勾的列和没有打勾的行画画线,这就是最小盖0线。

5

0

2

0

2

 

2

3

0

0

0

 

0

10

5

7

2

9

8

0

0

4

 

0

6

3

6

5

         

5

0

2

0

2

 

2

3

0

0

0

 

0

10

5

7

2

9

8

0

0

4

 

0

6

3

6

5

         

5.更新矩阵:

(1)对没有被线划到的数中,找到最小的数。

(2)对没有被线划到的数中,减去最小的数。

(3)对被2条线划到的数中,加上最小的数。

7

0

2

0

2

4

3

0

0

0

0

8

3

5

0

11

8

0

0

4

0

4

1

4

3

6.重复3-5直到成功。

0

1

0

0

0

0

0

1

0

0

0

0

0

0

1

0

0

0

1

0

1

0

0

0

0

sum = 7+6+9+6+4 = 32

练习:http://soj.me/show_problem.php?pid=1002&cid=1085

注意题目是求最大值,所以需要对矩阵做一点处理。

代码:

#include <iostream>#include <algorithm>#include <cstring>#include <climits>using namespace std;

#define Max 17int n;                    //维数 int s[Max][Max];        //原始矩阵 int p[Max][Max];        //归约矩阵 int q[Max][Max];        //0:未被画线 1:画了1次 2: 画了2次(交点) int row[Max], col[Max];    //行列0元素个数 int r[Max][Max];        //0:非0元素 1:非独立0元素 2:独立0元素 int x[Max],y[Max];        //画线时是否被打勾,1是0不是 

//数每行每列的0元素个数 void countZero(){    memset(row, 0, sizeof(row));

    memset(col, 0, sizeof(col));

    memset(r, 0, sizeof(r));

for (int i = 0; i < n; ++i)    {for (int j = 0; j < n; ++j)        {if (p[i][j] == 0)                row[i]++, col[j]++;        }    }}

//画最少的线覆盖所有0元素 int drawLine(){        memset(q, 0, sizeof(q));

for (int i = 0; i < n; ++i)        x[i] = 1, y[i] = 0;

//row 对所有不含独立0元素的行打勾! for (int i = 0; i < n; ++i)    {for (int j = 0; j < n; ++j)        {if (r[i][j] == 2)            {                x[i] = 0;

break;            }        }    }

bool is = 1;while (is)    //循环直到没有勾可以打     {is = 0;//col 对打勾的行中含0元素的未打勾的列打勾 for (int i = 0; i < n; ++i)        {if (x[i] == 1)            {for (int j = 0; j < n; ++j)                {if (p[i][j] == 0 && y[j] == 0)                    {                        y[j] = 1;

is = 1;                    }                }            }        }

//row 对打勾的列中含独立0元素的未打勾的行打勾 for (int j = 0; j < n; ++j)        {if (y[j] == 1)            {for (int i = 0; i < n; ++i)                {if (p[i][j] == 0 && x[i] == 0 && r[i][j] == 2)                    {                        x[i] = 1;

is = 1;                    }                }            }        }    }

//没有打勾的行和有打勾的列画线,这就是覆盖所有0元素的最少直线数 int line = 0;for (int i = 0; i < n; ++i)    {if (x[i] == 0)        {for (int j = 0; j < n; ++j)                q[i][j]++;

            line++;        }

if (y[i] == 1)        {for (int j = 0; j < n; ++j)                q[j][i]++;

            line++;        }    }

return line;}

//找独立0元素个数 /*1.找含0最少的那一行/列    2.划掉,更新该行/列0元素所在位置的row[],col[]  3.直到所有0被划线,这里定义为row[]col[]全为INT_MAX,表示该行/列无0元素*/ int find(){    countZero();

int zero = 0;     //独立0元素的个数 

while (1)    {//row[i] = INT_MAX表示该行无0元素,防止与*min_element()冲突 for (int i = 0; i < n; ++i)        {if (row[i] == 0)                row[i] = INT_MAX;if (col[i] == 0)                col[i] = INT_MAX;        }

bool stop = 1;

if (*min_element(row, row+n) <= *min_element(col, col+n))    //行最少0元素         {//找含0最少的那一行 int tmp = INT_MAX, index = -1;for (int i = 0; i < n; ++i)            {if (tmp > row[i])                    tmp = row[i], index = i;            }

/*找该行任意一个没被划掉的0元素(独立0元素),找到一个就行*/ int index2 = -1;            //该行独立0元素的列值for (int i = 0; i < n; ++i)if (p[index][i] == 0 && col[i] != INT_MAX)                {                    index2 = i;                    stop = 0;            //找到独立0元素则继续循环                     zero++;                //独立0元素的个数 break;                }

//找不到独立0元素了 if (stop)    break;

//标记             row[index] = col[index2] = INT_MAX;                r[index][index2] = 1;                //独立0元素,等等会++. 

//更新其所在行列的col,rowfor (int i = 0; i < n; ++i)if (p[index][i] == 0 && col[i] != INT_MAX)    //若该行还有0且没被划掉才更新                     col[i]--;for (int i = 0; i < n; ++i)if (p[i][index2] == 0 && row[i] != INT_MAX)                    row[i]--;        }else                                                        //列最少0元素         {int tmp = INT_MAX, index = -1;for (int i = 0; i < n; ++i)            {if (tmp > col[i])                    tmp = col[i], index = i;            }

int index2 = -1;for (int i = 0; i < n; ++i)if (p[i][index] == 0 && row[i] != INT_MAX)                {                        index2 = i;                    stop = 0;                    zero++;break;                }

if (stop)break;

            row[index2] = col[index] = INT_MAX;            r[index2][index] = 1;

for (int i = 0; i < n; ++i)if (p[index2][i] == 0 && col[i] != INT_MAX)                    col[i]--;for (int i = 0; i < n; ++i)if (p[i][index] == 0 && row[i] != INT_MAX)                    row[i]--;        }    }//r[i][j] 0:非0元素 1:非独立0元素 2:独立0元素 for (int i = 0; i < n; ++i)for (int j = 0; j < n; ++j)if (p[i][j] == 0)                r[i][j]++;

return zero;}

int main(){int m;

    cin >> m;

while (m--)    {            cin >> n;

for (int i = 0; i < n; ++i)for (int j = 0; j < n; ++j)                cin >> s[i][j];

//行归约 for (int i = 0; i < n; ++i)for (int j = 0; j < n; ++j)                p[i][j] = *max_element(s[i], s[i]+n)-s[i][j];    //求和最大 //p[i][j] = s[i][j]-*min_element(s[i],s[i]+j);     //求和最小 

//列归约 for (int j = 0; j < n; ++j)        {int tmp = INT_MAX;for (int i = 0; i < n; ++i)            {if (tmp > p[i][j])                    tmp = p[i][j];            }for (int i = 0; i < n; ++i)                p[i][j] -= tmp;        }

while (find() < n)        {            drawLine();

//最小的未被划线的数int min = INT_MAX;for (int i = 0; i < n; ++i)for (int j = 0; j < n; ++j)    if (q[i][j] == 0 && min > p[i][j])                        min = p[i][j];

//更新未被划到的和交点 for (int i = 0; i < n; ++i)for (int j = 0; j < n; ++j)if (q[i][j] == 0)                        p[i][j] -= min;else if (q[i][j] == 2)                        p[i][j] += min;                }

//求和 int ans = 0;for (int i = 0; i < n; ++i)for (int j = 0; j < n; ++j)if (r[i][j] == 2)                    ans += s[i][j];

        cout << ans << endl;    }}

View Code

指派问题与匈牙利解法相关推荐

  1. 平衡运输问题及其表上作业法---指派问题及其匈牙利解法

    一.平衡运输问题及其表上作业法 平衡问题及数学建模  平衡运输问题: 就是生产数量与销售数量相等的运输问题.对总产量等于总需求量的运输问题,可直接采用表上作业法求最优运输方案  数学模型:  2.表上 ...

  2. 利用python解决指派问题(匈牙利算法)

    前言:最近在备战数模,看到了指派问题,饶有兴趣,百度上找了很多关于指派问题的解法,很明显用的是匈牙利算法,手工的计算,各种各种的博客都很详尽,大概都了解了具体原理(不懂的人可以自行百度),但是基本找不 ...

  3. C#实现指派问题的匈牙利算法(运筹学)

    代码平台 VS2019(32位) + Office2019(64位) 注意事项 1.运行过程中出现未注册JET.OLEDB.12.0错误信息,需要下载AccessDatabaseEngine数据访问组 ...

  4. 指派问题与匈牙利算法

    一.问题引入 指派问题 有n项不同的工作或任务,需要n个人去完成,要求每人只完成一项工作.由于每人的知识.能力.经验等不同,故各人完成不同任务所需的时间不同.问应指派何人完成何项工作,使完成n项工作总 ...

  5. 指派问题(匈牙利算法)

    问题描述: 在生活中经常遇到这样的问题,某单位需完成n项任务,恰好有n个人可承担这些任务.由于每人的专长不同,各人完成任务不同(或所费时间),效率也不同.于是产生应指派哪个人去完成哪项任务,使完成n项 ...

  6. Java指派问题_指派问题的匈牙利算法

    '========================================= '作者:大漠.jxzhoumin '======================================= ...

  7. 学习匈牙利算法解决指派问题

    指派问题 指派问题的标准形式 指派问题的数学模型 非标准形式的指派问题 指派问题的匈牙利解法的一般步骤 以上步骤并不好理解下面进行一些实例展示方便理解 匈牙利解法的实例 这是一个比较友好的例子,一切按 ...

  8. 数学建模 匈牙利算法求解整数规划基本原理与编程实现

    投资问题(0-1规划) 匈牙利算法求解0-1规划问题 解答: 项目之间是互斥关系,所以使用x1+x2+x3=1; 项目5是以项目1为先验条件,所以x5<=x1,意味着x1=1时,x5=1或0 , ...

  9. SORT跟踪算法的详细解释,不容错过

    转载自:https://blog.csdn.net/HaoBBNuanMM/article/details/85555547 SORT - SIMPLE ONLINE AND REALTIME TRA ...

最新文章

  1. 每天进步:2014年10月几篇
  2. Js操作表格-对表格单元格的添加删除修改
  3. Ajax的使用四大步骤详解,五、使用Ajax的步骤
  4. OpenCV平滑图像Smoothing Images
  5. java bmp rgb数组_将RGB数据写入BMP位图文件
  6. BZOJ 2004 公交线路(状压DP+矩阵快速幂)
  7. 从招行数据架构调整,详解企业急需的数据中台与5大数字化转型
  8. 一些不起眼但非常有用的 Vim 命令
  9. JS生成数字下拉列表
  10. 【hadoop】java 获取 yarn app 信息 报错 ConfiguredRMFailoverProxyProvider - Failing over to rm2
  11. 转载:公司招聘中不能说的秘密
  12. Linux下通过 rm -f 删除大量文件时报错:Argument list too long
  13. html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形
  14. 《爱的五种能力》读书笔记
  15. IntelliJ IDEA设置方法注释模板并解决入参和返回值为空问题
  16. 计算机网络(一)网络分层及协议
  17. 【Optimizaition/x86】Intel CPU的CPUID指令获取的C实现
  18. 网络营销推广落地方案(2018最新)
  19. What is Java thread priority? 什么是java线程优先级
  20. MATLAB散点密度图的画法三

热门文章

  1. 2021春招BAT面试真题详解,java数据字典开发实例
  2. Flink1.13.2运行在大数据星环TDH集群的jdk1.7上解决办法
  3. matlab:GS算法
  4. react网格生成_如何在React中构建实时可编辑数据网格
  5. 数据库中的触发器是用来做什么的?
  6. 原因分析与解决方案的实战演练
  7. 关于电子标准机柜的“U”和“19英寸”
  8. Java版电子招投标系统源码之了解电子招标投标全流程
  9. 充分挖掘 AI 潜能,看英特尔® AMX 如何为千行百业注智赋能
  10. java 通过jacob调用bartender的坑