[算法设计]工作分配问题

工作分配问题

题目

设有n件工作分配给n个人。将第i份工作分配给第j个人所需的费用为c。试设计一个算法,为每一个人都分配1件不同的工作,并使总费用达到最小。

分析

深度优先搜索(dfs)
可以将本题视为人选工作或者工作“选人”,可以用一棵二叉树例举出所有情况,树的高度即表示第几份工作,每一个节点即表示一个人。

在这里插入图片描述

思路
  • dfs:深度遍历二叉树,当到达叶子节点时,如果该路径为最短路径则更新最小代价并更新最短路径。
  • 回溯:在搜索过程中,如果发现当前分配方案不能导致更优解,需要撤销(回溯)之前的分配决定,重新为该人分配其他任务。(用st数组来存是否被使用)
优化
  • 剪枝:进行到某一步时,如果其代价已经大于最小代价,则不再需要计算该二叉树分支以下的部分,即进行剪枝。
  • 记录路径:每次用一个数组(每次重新记录路径时会从数组起始下标记录,会不断覆盖原数组)去记录当前方案的路径,当数组记录完成最后一步时,将该路径记录到最短路径中(即更新最短路径)。
程序设计
  1. 数组说明
    • g 表示代价矩阵
    • st 用来标记某一个人是否被选择,确保每个人只被分配一次任务,因为问题要求为每个人分配一个不同的任务。
    • n 工作及工人的数量
    • Min 最小代价
    • sum 计算过程中的代价和
    • s 计算过程中记录路径
    • p 最短路径
  2. 代码分析
#include <iostream>

using namespace std;

const int N = 110;

int g[N][N];
int st[N];
int n, Min;
int sum;
int s[N] ,p[N];

void dfs(int x)           //x表示树的第x层
{
    if(x > n)              //遍历到叶子节点
    {
        if(sum < Min)      
        {
           Min = sum;       //更新最小代价
           for(int i = 1; i <= n; i++) p[i] = s[i];     //更新最短路径
           return;
        }
    }
    for(int i = 1; i <= n; i++)     //遍历截止到第i层未被选择的人
    {
        if(!st[i])         //如果该点未被使用
        {
            st[i] = 1;    //标记第i个人已经被“选”
            sum += g[x][i];      //更新代价
            s[x] = i;       //记录路径
            if(sum < Min) dfs(x + 1);    //进行剪枝  
            st[i] = 0;                  //回溯-撤销之前的分配
            sum -= g[x][i];         //回溯-撤销之前的分配
        }
    }
}
int main()
{
    Min = 1e8;       //初始化Min为无穷大
    cin >> n;
    for(int i = 1; i <= n; i++)
     for(int j = 1; j <= n; j++)
      cin >> g[i][j];          //初始化代价矩阵
      
    dfs(1);           //从第一层开始遍历
    cout << "分配关系为:" << endl;   
    int cnt = 1;
    for(int i = 1; i <= n; i++) cout << "第" << cnt++ <<"个任务应该分配给第" << p[i] << " 个人" << endl;
    cout << endl;
    cout << "所需最少时间为:" << Min;
    return 0;
}

运行过程如下图

在这里插入图片描述

在这里插入图片描述

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值