作业分配问题-回溯法-Python3

题目要求:

      有n份作业分配给n个人去完成,每人完成一份作业。假定第i个人完成第j份作业需要花费cij时间, cij>0,1≦i,j≦n。试设计一个回溯算法,将n份作业分配给n个人完成,使得总花费时间最短。

题目分析:

 

      由于每个人都必须分配到工作,在这里可以建一个二维数组C[k][i],用以表示k号工人完成i号工作所需的费用。给定一个循环,从第1个工人开始循环分配工作,直到所有工人都分配到。为第k个工人分配工作时,再循环检查每个工作是否已被分配,没有则分配给k号工人,否则检查下一个工作。可以用一个二维数组t[k][j]来表示第i号工作是否被k号人分配,未分配则x[k][i]=0,否则x[k][i]=1。利用回溯思想,在工人循环结束后回到上一工人,取消此次分配的工作,而去分配下一工作直到可以分配为止。这样,一直回溯到第1个工人后,就能得到所有的可行解。在检查工作分配时,其实就是判断取得可行解时的二维数组的一下标都不相同,二下标同样不相同。(我用的递归实现,也可以用栈来实现)

Python3 实现:

# @Time   :2018/5/8
# @Author :LiuYinxing

class Solution:

    def __init__(self, p):
        self.n = len(p)  # 获取n大小
        self.minP = float("inf")
        self.tempMinP = 0
        self.p = p  # 人员花费时间表
        self.t = [[0] * self.n for _ in range(self.n)]  # 标记数组
        self.path = None
        self.b = 0  # 记录是否已经走完一趟(0 无,1 走完一次)

    def isok(self, k, i):
        for j in range(self.n):
            if self.t[k][j] == 1: return False  # 第k人是否已经分配工作
        for j in range(self.n):
            if self.t[j][i] == 1: return False  # 第i项工作是否已经被分配
        return True


    def getMinP(self, k=0):
        if self.b == 1 and self.tempMinP >= self.minP: return  # 进行减枝
        if k == self.n:
            self.b == 1
            if self.tempMinP < self.minP:
                self.minP = self.tempMinP
                self.path = [v.index(1) + 1 for v in self.t]  # 记录路径
            return
        for i in range(self.n):
            if self.isok(k, i):
                self.tempMinP += self.p[k][i]
                self.t[k][i] = 1
                self.getMinP(k + 1)
                self.tempMinP -= self.p[k][i]
                self.t[k][i] = 0

    def getRul(self):
        self.getMinP()
        print('人员依次选择的工作为:',self.path)
        print('最短时间为:',self.minP)

if __name__ == '__main__':
    p = [[10, 2, 3], [2, 3, 4], [3, 4, 5]]
    solu = Solution(p)
    solu.getRul()

Python3实现,使用了numpy

# @Time   :2018/5/8
# @Author :LiuYinxing

import numpy as np

class Solution:
    def __init__(self, p):
        self.n = len(p)  # 获取n大小
        self.minP = float("inf")  # 记录最小的 花费
        self.tempMinP = 0  # 保存中间值
        self.p = p      # 人员花费时间表
        self.t = np.zeros((self.n, self.n))  # 标记数组全为零,
        self.b = 0      # 记录是否已经走完一趟(0 无,1 走完一次)
        self.path = []  # 负责记录路径


    def getMinP(self, k=0):
        if self.b == 1 and self.tempMinP >= self.minP: return  # 进行减枝
        if k == self.n:
            self.b = 1
            if self.tempMinP < self.minP:
                self.minP = self.tempMinP
                self.path = [np.argmax(v)+1 for v in self.t]
            return

        for i in range(self.n):
            if (1 not in self.t[k]) and (1 not in self.t[:,i]):  # 判断第k人是否已经分配工作,第i项工作是否已经被分配
                self.tempMinP += self.p[k][i]  # 添加一个值
                self.t[k, i] = 1               # 标记此位置已经走过
                self.getMinP(k + 1)            # 添加下一个人员
                self.tempMinP -= self.p[k][i]  # 恢复中间变量
                self.t[k, i] = 0               # 恢复位置

    def getRul(self):
        self.getMinP()
        print('人员依次选择的工作为:',self.path)
        print('最短时间为:',self.minP)

if __name__ == '__main__':
    p = [[10,2,3], [2,3,4], [3,4,5]]  # 9
    # p = [[50,43,1,58,60], [87,22,5,62,71], [62,98,97,27,38], [56,57,96,73,71], [92,36,43,27,95]]
    # p = [[50,43,1,58], [87,22,5,62], [62,98,97,27], [56,57,96,73]]
    # p = [[8,6,13,4], [9,5,7,15], [5,2,7,11], [14,12,16,13]]
    # p = [[85,45,12,16], [71,59,86,41], [88,45,23,76], [86,78,39,51]]
    solu = Solution(p)
    solu.getRul()

发现问题,可以留言指教哦。小白一个。

    原文作者:回溯法
    原文地址: https://blog.csdn.net/xx_123_1_rj/article/details/80234097
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞