Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit eaf895d

Browse files
feat: add task assignment problem using bitmasking and DP (TheAlgorithms#958)
1 parent 1ce9f3a commit eaf895d

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed

‎DIRECTORY.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
* [Rod Cutting](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/rod_cutting.rs)
106106
* [Snail](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/snail.rs)
107107
* [Subset Generation](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/subset_generation.rs)
108+
* [Task Assignment](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/task_assignment.rs)
108109
* [Subset Sum](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/subset_sum.rs)
109110
* [Trapped Rainwater](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/trapped_rainwater.rs)
110111
* [Word Break](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/word_break.rs)

‎src/dynamic_programming/mod.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod rod_cutting;
1717
mod snail;
1818
mod subset_generation;
1919
mod subset_sum;
20+
mod task_assignment;
2021
mod trapped_rainwater;
2122
mod word_break;
2223

@@ -47,5 +48,6 @@ pub use self::rod_cutting::rod_cut;
4748
pub use self::snail::snail;
4849
pub use self::subset_generation::list_subset;
4950
pub use self::subset_sum::is_sum_subset;
51+
pub use self::task_assignment::count_task_assignments;
5052
pub use self::trapped_rainwater::trapped_rainwater;
5153
pub use self::word_break::word_break;
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Task Assignment Problem using Bitmasking and DP in Rust
2+
// Time Complexity: O(2^M * N) where M is number of people and N is number of tasks
3+
// Space Complexity: O(2^M * N) for the DP table
4+
5+
use std::collections::HashMap;
6+
7+
/// Solves the task assignment problem where each person can do only certain tasks,
8+
/// each person can do only one task, and each task is performed by only one person.
9+
/// Uses bitmasking and dynamic programming to count total number of valid assignments.
10+
///
11+
/// # Arguments
12+
/// * `task_performed` - A vector of vectors where each inner vector contains tasks
13+
/// that a person can perform (1-indexed task numbers)
14+
/// * `total_tasks` - The total number of tasks (N)
15+
///
16+
/// # Returns
17+
/// * The total number of valid task assignments
18+
pub fn count_task_assignments(task_performed: Vec<Vec<usize>>, total_tasks: usize) -> i64 {
19+
let num_people = task_performed.len();
20+
let dp_size = 1 << num_people;
21+
22+
// Initialize DP table with -1 (uncomputed)
23+
let mut dp = vec![vec![-1; total_tasks + 2]; dp_size];
24+
25+
let mut task_map = HashMap::new();
26+
let final_mask = (1 << num_people) - 1;
27+
28+
// Build the task -> people mapping
29+
for (person, tasks) in task_performed.iter().enumerate() {
30+
for &task in tasks {
31+
task_map.entry(task).or_insert_with(Vec::new).push(person);
32+
}
33+
}
34+
35+
// Recursive DP function
36+
fn count_ways_until(
37+
dp: &mut Vec<Vec<i64>>,
38+
task_map: &HashMap<usize, Vec<usize>>,
39+
final_mask: usize,
40+
total_tasks: usize,
41+
mask: usize,
42+
task_no: usize,
43+
) -> i64 {
44+
// Base case: all people have been assigned tasks
45+
if mask == final_mask {
46+
return 1;
47+
}
48+
49+
// Base case: no more tasks available but not all people assigned
50+
if task_no > total_tasks {
51+
return 0;
52+
}
53+
54+
// Return cached result if already computed
55+
if dp[mask][task_no] != -1 {
56+
return dp[mask][task_no];
57+
}
58+
59+
// Option 1: Skip the current task
60+
let mut total_ways =
61+
count_ways_until(dp, task_map, final_mask, total_tasks, mask, task_no + 1);
62+
63+
// Option 2: Assign current task to a capable person who isn't busy
64+
if let Some(people) = task_map.get(&task_no) {
65+
for &person in people {
66+
// Check if this person is already assigned a task
67+
if mask & (1 << person) != 0 {
68+
continue;
69+
}
70+
71+
// Assign task to this person and recurse
72+
total_ways += count_ways_until(
73+
dp,
74+
task_map,
75+
final_mask,
76+
total_tasks,
77+
mask | (1 << person),
78+
task_no + 1,
79+
);
80+
}
81+
}
82+
83+
// Cache the result
84+
dp[mask][task_no] = total_ways;
85+
total_ways
86+
}
87+
88+
// Start recursion with no people assigned and first task
89+
count_ways_until(&mut dp, &task_map, final_mask, total_tasks, 0, 1)
90+
}
91+
92+
#[cfg(test)]
93+
mod tests {
94+
use super::*;
95+
96+
// Macro to generate multiple test cases for the task assignment function
97+
macro_rules! task_assignment_tests {
98+
($($name:ident: $input:expr => $expected:expr,)*) => {
99+
$(
100+
#[test]
101+
fn $name() {
102+
let (task_performed, total_tasks) = $input;
103+
assert_eq!(count_task_assignments(task_performed, total_tasks), $expected);
104+
}
105+
)*
106+
};
107+
}
108+
109+
task_assignment_tests! {
110+
test_case_1: (vec![vec![1, 3, 4], vec![1, 2, 5], vec![3, 4]], 5) => 10,
111+
test_case_2: (vec![vec![1, 2], vec![1, 2]], 2) => 2,
112+
test_case_3: (vec![vec![1], vec![2], vec![3]], 3) => 1,
113+
test_case_4: (vec![vec![1, 2, 3], vec![1, 2, 3], vec![1, 2, 3]], 3) => 6,
114+
test_case_5: (vec![vec![1], vec![1]], 1) => 0,
115+
116+
// Edge test case
117+
test_case_single_person: (vec![vec![1, 2, 3]], 3) => 3,
118+
}
119+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /