题目要求:
有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()
发现问题,可以留言指教哦。小白一个。