1
+ """
2
+
3
+ This is a Python implementation for questions involving task assignments between people.
4
+ Here Bitmasking and DP are used for solving this.
5
+
6
+ Question :-
7
+ We have N tasks and M people. Each person in M can do only certain of these tasks. Also
8
+ a person can do only one task and a task is performed only by one person.
9
+ Find the total no of ways in which the tasks can be distributed.
10
+ """
11
+
12
+ from collections import defaultdict
13
+
14
+
15
+ class AssignmentUsingBitmask :
16
+ def __init__ (self , task_performed , total ):
17
+ self .total_tasks = total # total no of tasks (N)
18
+
19
+ # DP table will have a dimension of (2^M)*N
20
+ # initially all values are set to -1
21
+ self .dp = [
22
+ [- 1 for i in range (total + 1 )] for j in range (2 ** len (task_performed ))
23
+ ]
24
+
25
+ self .task = defaultdict (list ) # stores the list of persons for each task
26
+
27
+ # final_mask is used to check if all persons are included by setting all bits
28
+ # to 1
29
+ self .final_mask = (1 << len (task_performed )) - 1
30
+
31
+ def count_ways_until (self , mask , task_no ):
32
+ # if mask == self.finalmask all persons are distributed tasks, return 1
33
+ if mask == self .final_mask :
34
+ return 1
35
+
36
+ # if not everyone gets the task and no more tasks are available, return 0
37
+ if task_no > self .total_tasks :
38
+ return 0
39
+
40
+ # if case already considered
41
+ if self .dp [mask ][task_no ] != - 1 :
42
+ return self .dp [mask ][task_no ]
43
+
44
+ # Number of ways when we don't this task in the arrangement
45
+ total_ways_util = self .count_ways_until (mask , task_no + 1 )
46
+
47
+ # now assign the tasks one by one to all possible persons and recursively
48
+ # assign for the remaining tasks.
49
+ if task_no in self .task :
50
+ for p in self .task [task_no ]:
51
+ # if p is already given a task
52
+ if mask & (1 << p ):
53
+ continue
54
+
55
+ # assign this task to p and change the mask value. And recursively
56
+ # assign tasks with the new mask value.
57
+ total_ways_util += self .count_ways_until (mask | (1 << p ), task_no + 1 )
58
+
59
+ # save the value.
60
+ self .dp [mask ][task_no ] = total_ways_util
61
+
62
+ return self .dp [mask ][task_no ]
63
+
64
+ def count_no_of_ways (self , task_performed ):
65
+ # Store the list of persons for each task
66
+ for i in range (len (task_performed )):
67
+ for j in task_performed [i ]:
68
+ self .task [j ].append (i )
69
+
70
+ # call the function to fill the DP table, final answer is stored in dp[0][1]
71
+ return self .count_ways_until (0 , 1 )
72
+
73
+
74
+ if __name__ == "__main__" :
75
+ total_tasks = 5 # total no of tasks (the value of N)
76
+
77
+ # Example 1
78
+ task_performed = [[1 , 3 , 4 ], [1 , 2 , 5 ], [3 , 4 ]]
79
+ print (
80
+ AssignmentUsingBitmask (task_performed , total_tasks ).count_no_of_ways (
81
+ task_performed
82
+ )
83
+ )
84
+
85
+ # Example 2
86
+ task_performed = [[1 , 2 ], [2 , 3 ], [1 , 3 ]]
87
+ print (
88
+ AssignmentUsingBitmask (task_performed , total_tasks ).count_no_of_ways (
89
+ task_performed
90
+ )
91
+ )
92
+
93
+ # Example 3
94
+ task_performed = [[1 , 2 , 3 ], [2 , 3 , 4 ], [1 , 4 , 5 ]]
95
+ print (
96
+ AssignmentUsingBitmask (task_performed , total_tasks ).count_no_of_ways (
97
+ task_performed
98
+ )
99
+ )
100
+
101
+ # Example 4
102
+ task_performed = [[1 ], [2 ], [3 ], [4 ], [5 ]]
103
+ print (
104
+ AssignmentUsingBitmask (task_performed , total_tasks ).count_no_of_ways (
105
+ task_performed
106
+ )
107
+ )
108
+
109
+ # Example 5
110
+ task_performed = [[1 , 2 , 3 , 4 , 5 ], [1 , 2 , 3 , 4 , 5 ], [1 , 2 , 3 , 4 , 5 ]]
111
+ print (
112
+ AssignmentUsingBitmask (task_performed , total_tasks ).count_no_of_ways (
113
+ task_performed
114
+ )
115
+ )
116
+
117
+ # the list of tasks that can be done by M persons.
118
+ task_performed = [[1 , 3 , 4 ], [1 , 2 , 5 ], [3 , 4 ]]
119
+ print (
120
+ AssignmentUsingBitmask (task_performed , total_tasks ).count_no_of_ways (
121
+ task_performed
122
+ )
123
+ )
0 commit comments