2019-03-27 17:59:31 +00:00
|
|
|
"""
|
|
|
|
|
2020-03-04 12:40:28 +00:00
|
|
|
This is a Python implementation for questions involving task assignments between people.
|
2019-03-27 17:59:31 +00:00
|
|
|
Here Bitmasking and DP are used for solving this.
|
|
|
|
|
|
|
|
Question :-
|
|
|
|
We have N tasks and M people. Each person in M can do only certain of these tasks. Also a person can do only one task and a task is performed only by one person.
|
|
|
|
Find the total no of ways in which the tasks can be distributed.
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
|
|
|
|
class AssignmentUsingBitmask:
|
2019-10-05 05:14:13 +00:00
|
|
|
def __init__(self, task_performed, total):
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
self.total_tasks = total # total no of tasks (N)
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-03-27 17:59:31 +00:00
|
|
|
# DP table will have a dimension of (2^M)*N
|
|
|
|
# initially all values are set to -1
|
2019-10-05 05:14:13 +00:00
|
|
|
self.dp = [
|
|
|
|
[-1 for i in range(total + 1)] for j in range(2 ** len(task_performed))
|
|
|
|
]
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
self.task = defaultdict(list) # stores the list of persons for each task
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2020-03-04 12:40:28 +00:00
|
|
|
# final_mask is used to check if all persons are included by setting all bits to 1
|
|
|
|
self.final_mask = (1 << len(task_performed)) - 1
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2020-03-04 12:40:28 +00:00
|
|
|
def CountWaysUtil(self, mask, task_no):
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-03-27 17:59:31 +00:00
|
|
|
# if mask == self.finalmask all persons are distributed tasks, return 1
|
2020-03-04 12:40:28 +00:00
|
|
|
if mask == self.final_mask:
|
2019-03-27 17:59:31 +00:00
|
|
|
return 1
|
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
# if not everyone gets the task and no more tasks are available, return 0
|
2020-03-04 12:40:28 +00:00
|
|
|
if task_no > self.total_tasks:
|
2019-03-27 17:59:31 +00:00
|
|
|
return 0
|
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
# if case already considered
|
2020-03-04 12:40:28 +00:00
|
|
|
if self.dp[mask][task_no] != -1:
|
|
|
|
return self.dp[mask][task_no]
|
2019-03-27 17:59:31 +00:00
|
|
|
|
2020-01-18 12:24:33 +00:00
|
|
|
# Number of ways when we don't this task in the arrangement
|
2020-03-04 12:40:28 +00:00
|
|
|
total_ways_util = self.CountWaysUtil(mask, task_no + 1)
|
2019-03-27 17:59:31 +00:00
|
|
|
|
|
|
|
# now assign the tasks one by one to all possible persons and recursively assign for the remaining tasks.
|
2020-03-04 12:40:28 +00:00
|
|
|
if task_no in self.task:
|
|
|
|
for p in self.task[task_no]:
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-03-27 17:59:31 +00:00
|
|
|
# if p is already given a task
|
2019-10-05 05:14:13 +00:00
|
|
|
if mask & (1 << p):
|
2019-03-27 17:59:31 +00:00
|
|
|
continue
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-03-27 17:59:31 +00:00
|
|
|
# assign this task to p and change the mask value. And recursively assign tasks with the new mask value.
|
2020-03-04 12:40:28 +00:00
|
|
|
total_ways_util += self.CountWaysUtil(mask | (1 << p), task_no + 1)
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-03-27 17:59:31 +00:00
|
|
|
# save the value.
|
2020-03-04 12:40:28 +00:00
|
|
|
self.dp[mask][task_no] = total_ways_util
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2020-03-04 12:40:28 +00:00
|
|
|
return self.dp[mask][task_no]
|
2019-03-27 17:59:31 +00:00
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
def countNoOfWays(self, task_performed):
|
2019-03-27 17:59:31 +00:00
|
|
|
|
|
|
|
# Store the list of persons for each task
|
|
|
|
for i in range(len(task_performed)):
|
|
|
|
for j in task_performed[i]:
|
|
|
|
self.task[j].append(i)
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-03-27 17:59:31 +00:00
|
|
|
# call the function to fill the DP table, final answer is stored in dp[0][1]
|
2019-10-05 05:14:13 +00:00
|
|
|
return self.CountWaysUtil(0, 1)
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-03-27 17:59:31 +00:00
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
if __name__ == "__main__":
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
total_tasks = 5 # total no of tasks (the value of N)
|
2019-08-19 13:37:49 +00:00
|
|
|
|
2019-10-05 05:14:13 +00:00
|
|
|
# the list of tasks that can be done by M persons.
|
|
|
|
task_performed = [[1, 3, 4], [1, 2, 5], [3, 4]]
|
|
|
|
print(
|
|
|
|
AssignmentUsingBitmask(task_performed, total_tasks).countNoOfWays(
|
|
|
|
task_performed
|
|
|
|
)
|
|
|
|
)
|
2019-03-27 17:59:31 +00:00
|
|
|
"""
|
|
|
|
For the particular example the tasks can be distributed as
|
|
|
|
(1,2,3), (1,2,4), (1,5,3), (1,5,4), (3,1,4), (3,2,4), (3,5,4), (4,1,3), (4,2,3), (4,5,3)
|
|
|
|
total 10
|
2019-10-05 05:14:13 +00:00
|
|
|
"""
|