我认为你可以通过解决最小成本流问题来最佳地解决这个问题。
下面的 Python 代码说明了如何使用 networkx 库为 3 名学生完成此操作。
import networkx as nx
G=nx.DiGraph()
multiple_prefs=[{'Tom':['Prof1','Prof2','Prof3'],
'Dick':['Prof2','Prof1','Prof3'],
'Harry':['Prof1','Prof3','Prof1']},
{'Tom':['Prof2','Prof1','Prof3'],
'Dick':['Prof2','Prof1','Prof3'],
'Harry':['Prof1','Prof3','Prof1']}
]
workload={'Prof1':2,'Prof2':10,'Prof3':4}
num_tests=len(multiple_prefs)
num_students=len(multiple_prefs[0])
G.add_node('dest',demand=num_tests*num_students)
A=[]
for i,prefs in enumerate(multiple_prefs):
testname='_test%d'%i
for student,proflist in prefs.items():
node=student+testname
A.append(node)
G.add_node(node,demand=-1)
for i,prof in enumerate(proflist):
if i==0:
cost=0 # happy to assign first choices
elif i==1:
cost=1 # Slightly unhappy to assign second choices
else:
cost=4 # Very unhappy to assign third choices
n2=prof+'_with_'+student
n3=prof
G.add_edge(node,n2,capacity=1,weight=cost) # Edge taken if student takes this test with the professor
G.add_edge(n2,n3,capacity=1,weight=0) # Capacity stops student taking both tests with the same professor
G.add_edge(n3,'dest',capacity=workload[prof],weight=0) # Capacity stops professor exceeding his workload
flowdict = nx.min_cost_flow(G)
for student in A:
for prof,flow in flowdict[student].items():
if flow:
print student,'with',prof
关键是我们为学生和教授的每个组合都有一个单独的节点(在代码中称为 n2)。通过将 n2 的容量设为 1,我们确保每个学生只能与该教授一起参加一次测试。
字典 multiple_prefs 存储两个字典。第一个字典包含每个学生第一次测试的偏好列表。第二个有第二个测试的偏好。
编辑
该代码现在还包括每个教授的节点 n3。这些节点和 dest 之间边缘的容量将限制每个教授的最大工作量。
字典工作量存储每个教授允许的最大测试数。