2019-03-27 23:29:31 +05:30
"""
This is a python implementation for questions involving task assignments between people.
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 01:14:13 -04:00
def __init__ ( self , task_performed , total ) :
2019-08-19 15:37:49 +02:00
2019-10-05 01:14:13 -04:00
self . total_tasks = total # total no of tasks (N)
2019-08-19 15:37:49 +02:00
2019-03-27 23:29:31 +05:30
# DP table will have a dimension of (2^M)*N
# initially all values are set to -1
2019-10-05 01:14:13 -04:00
self . dp = [
[ - 1 for i in range ( total + 1 ) ] for j in range ( 2 * * len ( task_performed ) )
]
2019-08-19 15:37:49 +02:00
2019-10-05 01:14:13 -04:00
self . task = defaultdict ( list ) # stores the list of persons for each task
2019-08-19 15:37:49 +02:00
2019-10-05 01:14:13 -04:00
# finalmask is used to check if all persons are included by setting all bits to 1
self . finalmask = ( 1 << len ( task_performed ) ) - 1
2019-08-19 15:37:49 +02:00
2019-10-05 01:14:13 -04:00
def CountWaysUtil ( self , mask , taskno ) :
2019-08-19 15:37:49 +02:00
2019-03-27 23:29:31 +05:30
# if mask == self.finalmask all persons are distributed tasks, return 1
if mask == self . finalmask :
return 1
2019-10-05 01:14:13 -04:00
# if not everyone gets the task and no more tasks are available, return 0
2019-03-27 23:29:31 +05:30
if taskno > self . total_tasks :
return 0
2019-10-05 01:14:13 -04:00
# if case already considered
if self . dp [ mask ] [ taskno ] != - 1 :
2019-03-27 23:29:31 +05:30
return self . dp [ mask ] [ taskno ]
2020-01-18 13:24:33 +01:00
# Number of ways when we don't this task in the arrangement
2019-10-05 01:14:13 -04:00
total_ways_util = self . CountWaysUtil ( mask , taskno + 1 )
2019-03-27 23:29:31 +05:30
# now assign the tasks one by one to all possible persons and recursively assign for the remaining tasks.
if taskno in self . task :
for p in self . task [ taskno ] :
2019-08-19 15:37:49 +02:00
2019-03-27 23:29:31 +05:30
# if p is already given a task
2019-10-05 01:14:13 -04:00
if mask & ( 1 << p ) :
2019-03-27 23:29:31 +05:30
continue
2019-08-19 15:37:49 +02:00
2019-03-27 23:29:31 +05:30
# assign this task to p and change the mask value. And recursively assign tasks with the new mask value.
2019-10-05 01:14:13 -04:00
total_ways_util + = self . CountWaysUtil ( mask | ( 1 << p ) , taskno + 1 )
2019-08-19 15:37:49 +02:00
2019-03-27 23:29:31 +05:30
# save the value.
self . dp [ mask ] [ taskno ] = total_ways_util
2019-08-19 15:37:49 +02:00
return self . dp [ mask ] [ taskno ]
2019-03-27 23:29:31 +05:30
2019-10-05 01:14:13 -04:00
def countNoOfWays ( self , task_performed ) :
2019-03-27 23:29:31 +05:30
# 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 15:37:49 +02:00
2019-03-27 23:29:31 +05:30
# call the function to fill the DP table, final answer is stored in dp[0][1]
2019-10-05 01:14:13 -04:00
return self . CountWaysUtil ( 0 , 1 )
2019-08-19 15:37:49 +02:00
2019-03-27 23:29:31 +05:30
2019-10-05 01:14:13 -04:00
if __name__ == " __main__ " :
2019-08-19 15:37:49 +02:00
2019-10-05 01:14:13 -04:00
total_tasks = 5 # total no of tasks (the value of N)
2019-08-19 15:37:49 +02:00
2019-10-05 01:14:13 -04: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 23:29:31 +05:30
"""
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 01:14:13 -04:00
"""