WalzoneInterview Prep
πŸ“ž Interviewing soon? Practice with a realistic AI mock phone interview β€” it calls you, then scores you. First 15 min FREE β†’

Dynamic Programming Β· Guru Β· question 89 of 100

Solve the problem of finding the Optimal Task Assignment with Different Time Intervals and Constraints using dynamic programming.?

πŸ“• Buy this interview preparation book: 100 Dynamic Programming questions & answers β€” PDF + EPUB for $5

Problem:

Given n tasks and m workers, where each worker has a different speed for performing each task. Each task must be assigned to exactly one worker, and each worker can perform at most one task at a time. The goal is to minimize the time it takes to complete all tasks. Additionally, there are certain constraints on the time intervals during which some tasks can be performed. Specifically, for each task, there is a minimum start time and a maximum end time. A task can only start after its minimum start time, and it must finish before its maximum end time. However, there are no limits on the amount of time a worker can spend on any task (other than the overall goal of minimizing the total time required).

Approach:

To solve this problem using dynamic programming, we will follow these steps:

1. Sort the tasks and workers by minimum start time in increasing order.

2. Define the state for our dynamic programming approach, which will be a set of binary variables indicating which tasks have been assigned to which workers.

3. We will iterate through each binary state, and for each state, we will calculate the time required to complete all tasks with the given state.

4. We will define our recurrence relation based on the previous step, which will calculate the minimum time required to complete all tasks from the previous state.

5. We will use memoization to store the minimum time required for each state.

6. We will return the minimum time required for the state where all tasks have been assigned to workers.

Example:

Let’s consider the following example:

Tasks: [T1, T2, T3, T4]
Minimum start times: [1, 5, 2, 10]
Maximum end times: [8, 15, 4, 20]
Workers: [W1, W2, W3, W4]
Task times for each worker: 
- W1: [3, 2, 4, 5]
- W2: [4, 3, 1, 6]
- W3: [2, 1, 3, 4]
- W4: [5, 3, 2, 4]

We start by sorting the tasks and workers by minimum start time:

Tasks: [T1, T3, T2, T4]
Minimum start times: [1, 2, 5, 10]
Maximum end times: [8, 4, 15, 20]
Workers: [W1, W3, W2, W4]
Task times for each worker: 
- W1: [3, 4, 2, 5]
- W3: [2, 1, 3, 4]
- W2: [4, 1, 3, 6]
- W4: [5, 2, 3, 4]

Next, we define our state to be a set of binary variables indicating which tasks have been assigned to which workers. For example, if task T1 is assigned to worker W1, task T2 is assigned to worker W3, and tasks T3 and T4 are not assigned to any worker, the state would be: [W1: T1, W2: empty, W3: T2, W4: empty].

We will iterate through each binary state, and for each state, we will calculate the time required to complete all tasks with the given state. We do this by calculating the maximum time required for each worker to complete their assigned tasks, and then taking the maximum of those times.

Our recurrence relation is based on this previous step. Specifically, for each task, we will try assigning it to each worker and calculate the total time required for the new state. We will then take the minimum of these times to get the overall minimum time required for completing all tasks.

We will use memoization to store the minimum time required for each state. We can represent this using a HashMap, where the key is the state and the value is the minimum time required to complete all tasks for that state.

Finally, we return the minimum time required for the state where all tasks have been assigned to workers.

Java Implementation:

import java.util.*;

public class OptimalTaskAssignment {
    
    static int n; // number of tasks
    static int m; // number of workers
    static int[] startTimes; // minimum start times for tasks
    static int[] endTimes; // maximum end times for tasks
    static int[][] times; // time required for each worker to complete each task
    static HashMap<String,Integer> memo; // memoization
    
    public static void main(String[] args) {
        n = 4;
        m = 4;
        startTimes = new int[]{1, 5, 2, 10};
        endTimes = new int[]{8, 15, 4, 20};
        times = new int[][]{{3, 2, 4, 5}, {4, 3, 1, 6}, {2, 1, 3, 4}, {5, 3, 2, 4}};
        memo = new HashMap<>();
        
        // sort tasks and workers by start time
        Task[] tasks = new Task[n];
        for (int i = 0; i < n; i++) {
            tasks[i] = new Task(i, startTimes[i], endTimes[i]);
        }
        Arrays.sort(tasks, new Comparator<Task>() {
            @Override
            public int compare(Task t1, Task t2) {
                return t1.startTime - t2.startTime;
            }
        });
        int[] taskOrder = new int[n];
        for (int i = 0; i < n; i++) {
            taskOrder[i] = tasks[i].id;
        }
        Worker[] workers = new Worker[m];
        for (int i = 0; i < m; i++) {
            workers[i] = new Worker(i, times[i]);
        }
        Arrays.sort(workers, new Comparator<Worker>() {
            @Override
            public int compare(Worker w1, Worker w2) {
                return w1.id - w2.id;
            }
        });
        
        // call dp function
        int[] assignedTasks = new int[n];
        Arrays.fill(assignedTasks, -1);
        int minTime = dp(assignedTasks, tasks, workers);
        System.out.println("Minimum time required: " + minTime);
    }
    
    static int dp(int[] assignedTasks, Task[] tasks, Worker[] workers) {
        String state = Arrays.toString(assignedTasks);
        if (memo.containsKey(state)) {
            return memo.get(state);
        }
        boolean allAssigned = true;
        for (int i = 0; i < n; i++) {
            if (assignedTasks[i] == -1) {
                allAssigned = false;
                break;
            }
        }
        if (allAssigned) {
            int time = 0;
            for (int i = 0; i < m; i++) {
                int workerTime = 0;
                for (int j = 0; j < n; j++) {
                    if (assignedTasks[tasks[j].id] == i) {
                        workerTime += workers[i].times[j];
                    }
                }
                time = Math.max(time, workerTime);
            }
            memo.put(state, time);
            return time;
        }
        int minTime = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            if (assignedTasks[tasks[i].id] == -1) {
                for (int j = 0; j < m; j++) {
                    boolean validAssignment = (tasks[i].startTime <= j && j <= tasks[i].endTime);
                    if (validAssignment) {
                        assignedTasks[tasks[i].id] = j;
                        int time = dp(assignedTasks, tasks, workers);
                        minTime = Math.min(minTime, time);
                        assignedTasks[tasks[i].id] = -1;
                    }
                }
            }
        }
        memo.put(state, minTime);
        return minTime;
    }
    
    static class Task {
        int id;
        int startTime;
        int endTime;
        public Task(int id, int startTime, int endTime) {
            this.id = id;
            this.startTime = startTime;
            this.endTime = endTime;
        }
    }
    
    static class Worker {
        int id;
        int[] times;
        public Worker(int id, int[] times) {
            this.id = id;
            this.times = times;
        }
    }
}

Complexity Analysis:

The time complexity of this dynamic programming approach is O(2nβ€…*β€…mβ€…*β€…n), where n is the number of tasks and m is the number of workers. This is because we have 2n different states (one binary variable for each task), and for each state, we iterate through n tasks and m workers. However, the memoization significantly reduces the actual number of computations we need to perform, so in practice the algorithm runs much faster than the worst-case complexity would suggest.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Dynamic Programming interview β€” then scores it.
πŸ“ž Practice Dynamic Programming β€” free 15 min
πŸ“• Buy this interview preparation book: 100 Dynamic Programming questions & answers β€” PDF + EPUB for $5

All 100 Dynamic Programming questions Β· All topics