對工做分配問題的求解

工做分配問題是一個典型的回溯問題,利用回溯思想能很準確地獲得問題的解。咱們就針對以下一個案例作一個系統的分析:算法

問題描述

\(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;
}
相關文章
相關標籤/搜索