对工做分配问题的求解

工做分配问题是一个典型的回溯问题,利用回溯思想能很准确地获得问题的解。咱们就针对以下一个案例作一个系统的分析:算法

问题描述

\(n\) 份工做要分配给 \(n\) 我的来完成,每一个人完成一份。第 \(i\) 我的完成第 \(k\) 份工做所用的时间为一个正整数 \(t_{ik}\),其中 \(1 \leq i, k \leq n\)。试肯定一个分配方案,使得完成这 \(n\) 份工做的时间总和最小。数组

输入包含 \(n + 1\) 行。优化

第 1 行为一个正整数 \(n\)spa

第 2 行到第 \(n + 1\) 行中每行都包含 \(n\) 个正整数,造成了一个 \(n \times n\) 的矩阵。在该矩阵中,第 \(i\) 行第 \(k\) 列元素 \(t_{ik}\) 表示第 \(i\) 我的完成第 \(k\) 件工做所要用的时间。code

输出为 1 行,包含一个正整数,表示全部分配方案中最小的时间总和。io

限制范围:class

\(1 \leq n \leq 15\)变量

\(1 \leq t_{ik} \leq 10^4\)循环

输入样例:gc

5
9 2 9 1 9
1 9 8 9 6
9 9 9 9 1
8 8 1 8 4
9 1 7 8 9

输出样例:

5

问题分析

因为每一个人都必须分配到工做,在这里能够建一个二维数组 time[i][j] ,用以表示 \(i\) 我的完成 \(j\) 号工做所花费的时间。给定一个循环,从第 1 我的开始循环分配工做,直到全部人都分配到。为第 \(i\) 我的分配工做时,再循环检查每一个工做是否已被分配,没有则分配给 \(i\) 我的,不然检查下一个工做。能够用一个一维数组 is_working[j] 来表示第 \(j\) 号工做是否已被分配,未分配则 is_working[j]=0 ,不然 is_working[j]=1 。利用回溯思想,在工人循环结束后回到上一工人,取消这次分配的工做,而去分配下一工做直到能够分配为止。这样,一直回溯到第 1 个工人后,就能获得全部的可行解。在检查工做分配时,其实就是判断取得可行解时的二维数组的第一维下标各不相同和第二维下标各不相同。而咱们是要获得完成这 \(n\) 份工做的最小时间总和,便可行解中和最小的一个,故须要再定义一个全局变量 cost_time_total_min 表示最终的时间总和,初始 cost_time_total_mintime[i][i] 之和,即对角线工做时间相加之和。在全部人分配完工做时,比较 \(count\)cost_time_total_min 的大小,若是 \(count\) 小于 cost_time_total_min ,证实在回溯时找到了一个最优解,此时就把 \(count\) 赋给 cost_time_total_min 。但考虑到算法的复杂度,这里还有一个剪枝优化的工做能够作。就是在每次计算局部费用变量 \(count\) 的值时,若是判断 \(count\) 已经大于 cost_time_total_min ,就不必再往下分配了,由于这时获得的解必然不是最优解。

实现代码

#include <cstdio>
#define N 16
int is_working[N] = {0};// 某项工做是否被分配
int time[N][N];// 完成某项工做所需的时间
int cost_time_total_min;// 完成 n 份工做的最小时间总和
// i 表示第几我的,count 表示工做费用总和
inline void work(int i, int count, int n){
    // 若是 i 超出了所能分配的最大工做件数,表示分配完成,而且 count 比原来 cost_time_total_min 花费少 则更新 cost_time_total_min 的值
    if(i > n && count < cost_time_total_min){
        cost_time_total_min = count;
        return;
    }
    // 回溯思想 
    if(count < cost_time_total_min){
        // j 表示第几件工做
        for(int j = 1 ; j <= n; j++){
            // 若是工做未被分配 is_working = 0
            if(is_working[j] == 0){
                // 分配工做 is_working = 1
                is_working[j] = 1;
                //工做交给第 i + 1 我的
                work(i + 1, count + time[i][j], n);
                //在一轮迭代完成以后,返回到上一我的,要对这次的工做进行从新分配,将 is_working[j] 重设为 0
                is_working[j] = 0;
            }
        }
    }
}
int main(int argc, char const *argv[])
{
    setvbuf(stdin, new char[1 << 20], _IOFBF, 1 << 20);
    setvbuf(stdout, new char[1 << 20], _IOFBF, 1 << 20);
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            scanf("%d", &time[i][j]);
        }
        cost_time_total_min += time[i][i];
    }
    work(1, 0, n);
    printf("%d\n", cost_time_total_min);
    return 0;
}
相关文章
相关标签/搜索