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