Solution For Task Scheduler Ii
Problem Statement:
You want to build a house on an empty land which reaches all buildings in the shortest amount of distance. You can only move up, down, left and right. You are given a 2D grid of values 0, 1 or 2, where:
Each 0 marks an empty land which you can pass by freely.
Each 1 marks a building which you cannot pass through.
Each 2 marks an obstacle which you cannot pass through.
Solution:
Approach: BFS (Breadth First Search)
Firstly, we need to determine whether it is possible to reach all the buildings from any given land. To do this, we will perform BFS traversal starting from each 1. For each 1, we will determine the distances d[i][j][k] to all lands that it can reach.
We will also keep a count c[i][j] which counts the number of 1s that we can reach from any given land. If we find a land i,j that can reach less than all the buildings, then we cannot build the house there as some buildings will not be reachable.
Algorithm Steps:
- Initialize 3D distance array d[i][j][k], 2D reachability array c[i][j], and total building count buildings.
- Traverse through the 2D grid array:
a. For each building do BFS of all reachable empty lands, updating distance array d.
b. For each reachable empty land at distance d[i][j][k], increment reachability count for this land in c[i][j].
c. Find the minimum distance to cover all buildings for each empty land point.
d. Choose the minimum of the empty land points with the minimum distance. - Return the minimum distance calculated in step 2(d).
Let’s implement this solution in Python.
Python Code:
class Solution:
def bfs(self, i, j, grid, d, bldgs):
# BFS traversal
q = [(i, j, 0)]
visited = set()
while q:
i, j, steps = q.pop(0)
if (i, j) in visited or i < 0 or j < 0 or i >= len(grid) or j >= len(grid[0]) or grid[i][j] == 2:
continue
visited.add((i, j))
if grid[i][j] == 1: # Building
bldgs.add((i, j)) # Add as a reachable building
d[i][j].append(steps) # Add distance to distance array
q.append((i+1, j, steps+1))
q.append((i-1, j, steps+1))
q.append((i, j+1, steps+1))
q.append((i, j-1, steps+1))
def shortestDistance(self, grid: List[List[int]]) -> int:
rows, cols = len(grid), len(grid[0])
d = [[[] for _ in range(cols)] for _ in range(rows)] # 3D array to store distances from each building to each land point
c = [[0] * cols for _ in range(rows)] # 2D array to store the number of buildings we can reach from each land point
bldgs = set() # Set of all building coordinates
# Traverse through the grid, BFS traversal from each building
for i in range(rows):
for j in range(cols):
if grid[i][j] == 1:
self.bfs(i, j, grid, d, bldgs)
# Traverse through the distance array and update c (reachability array)
for i in range(rows):
for j in range(cols):
if grid[i][j] == 0: # Reachable empty land
for k in range(len(d[i][j])):
c[i][j] += 1 # Increment count of reachable buildings
# Find minimum distance to cover all buildings
minimum = float('inf')
for i in range(rows):
for j in range(cols):
if grid[i][j] == 0 and c[i][j] == len(bldgs): # Reachable empty land with all buildings reachable
minimum = min(minimum, sum(d[i][j])) # Update minimum distance
return -1 if minimum == float('inf') else minimum
Time Complexity: O(n^2 * m^2) where n = number of rows and m = number of columns. We do BFS traversal for each building for each land point. Each BFS has time complexity O(nm) as we traverse through the entire grid.
Space Complexity: O(nm(r + c)) where r = maximum number of reachable buildings from a single empty land and c = maximum number of distances from a single building. We store a 3D distance array d which has n * m * r elements, a 2D reachability array c which has n * m elements and a set of all the building coordinates.
Step by Step Implementation For Task Scheduler Ii
import java.util.*; class Solution { public int leastInterval(char[] tasks, int n) { int[] map = new int[26]; for (char c: tasks) map[c - 'A']++; PriorityQueue < Integer > queue = new PriorityQueue < > (26, Collections.reverseOrder()); for (int f: map) { if (f > 0) queue.add(f); } int time = 0; while (!queue.isEmpty()) { int i = 0; List < Integer > temp = new ArrayList < > (); while (i <= n) { if (!queue.isEmpty()) { if (queue.peek() > 1) temp.add(queue.poll() - 1); else queue.poll(); } time++; if (queue.isEmpty() && temp.size() == 0) break; i++; } for (int l: temp) queue.add(l); } return time; } }
class Solution: def leastInterval(self, tasks: List[str], n: int) -> int: # Create a list of task frequencies task_freqs = collections.Counter(tasks).values() # Sort the list in descending order task_freqs.sort(reverse=True) # Get the max frequency max_freq = task_freqs[0] # Calculate the number of idle slots needed # The idea is to have as many tasks with the max frequency as possible # in each interval, with at least one task. # For example, if the tasks are ["A","A","A","B","B","B"], and n = 2, # we can schedule them like this: # A -> B -> idle -> A -> B -> idle -> A -> B # There are 2 intervals (A, B) with 3 tasks each and 2 intervals with 1 idle slot each. # In total, we need n * (max_freq - 1) + (max_freq - 1) + 1 = 8 slots. idle_slots = n * (max_freq - 1) # Fill the idle slots with tasks # We can use the remaining tasks to fill the idle slots. # For example, if the tasks are ["A","A","A","B","B","B"], and n = 2, # we can schedule them like this: # A -> B -> idle -> A -> B -> idle -> A -> B # There are 2 intervals (A, B) with 3 tasks each and 2 intervals with 1 idle slot each. # In total, we need n * (max_freq - 1) + (max_freq - 1) + 1 = 8 slots. for freq in task_freqs[1:]: # If the current task frequency is less than the max frequency, # we can use it to fill the idle slots if freq < max_freq: idle_slots -= freq # If there are more tasks than idle slots, we need to add more slots if idle_slots < 0: return len(tasks) + idle_slots # Otherwise, we can return the original number of tasks return len(tasks)
var scheduleTask = function(tasks, n) { // TODO: your code here // create a map to store the frequency of each task let map = {}; for (let t of tasks) { map[t] = (map[t] || 0) + 1; } // create an array of task frequencies let freqs = []; for (let key in map) { freqs.push(map[key]); } // sort the array in descending order freqs.sort((a, b) => b - a); // find the maximum number of task frequency let fmax = freqs[0]; // calculate the idle slots needed for the most frequent task let idle = (fmax - 1) * (n + 1); // fill the idle slots with the next most frequent tasks for (let f of freqs) { if (f === fmax) break; idle -= Math.min(fmax - 1, f); } // if there are more tasks than idle slots, return the length of the tasks array return idle > 0 ? idle + tasks.length : tasks.length; };
There are a total of n tasks you have to pick, labeled from 0 to n-1. Some tasks may have dependencies, for example, if task 0 depends on tasks 1, then you cannot pick task 0 first. You are given an array of integers nums, where nums[i] is the number of tasks that depends on task i. You should return the ordering of tasks you should pick to finish all tasks. If there is a task that can be done multiple times, you can pick it any number of times. One possible solution is to use topological sort. We can represent the dependencies between the tasks as a directed graph, where each node represents a task and each edge represents a dependency. Then, a valid ordering of the tasks is equivalent to a topological ordering of the corresponding directed graph. We can use a modified version of DFS to find a topological ordering of the graph. In each step of the DFS, we choose a node that has no remaining dependencies and add it to the ordering. We then delete that node and all of its edges from the graph. We repeat this process until there are no more nodes left in the graph. If there is a cycle in the graph, then there is no valid ordering of the tasks. We can detect a cycle by keeping track of which nodes are currently in the middle of the DFS. If we ever try to visit a node that is already in the middle of the DFS, then we have detected a cycle. def findOrder(self, numTasks, prerequisites): # Represent the dependencies between the tasks as a directed graph. graph = collections.defaultdict(list) for i, j in prerequisites: graph[j].append(i) # Keep track of which nodes are currently in the middle of the DFS. in_progress = set() # Keep track of the ordering of the tasks. ordering = [] def dfs(node): if node in in_progress: # There is a cycle, so this ordering is invalid. return False in_progress.add(node) for dependency in graph[node]: if not dfs(dependency): return False # We can finish this task now that all of its dependencies have # been completed. in_progress.remove(node) ordering.append(node) return True for node in range(numTasks): if not dfs(node): # There is a cycle, so this ordering is invalid. return [] return ordering
public class Solution { public int LeastInterval(char[] tasks, int n) { //Create a dictionary to store the count of each task DictionarytaskCount = new Dictionary (); foreach(char task in tasks){ if(!taskCount.ContainsKey(task)){ taskCount.Add(task, 1); } else{ taskCount[task]++; } } //Sort the dictionary in descending order of task count var sortedTaskCount = taskCount.OrderByDescending(x => x.Value); //Find the maximum task count int maxCount = sortedTaskCount.First().Value; //Find the number of tasks with the maximum count int maxCountTaskCount = sortedTaskCount.Where(x => x.Value == maxCount).Count(); //Calculate the minimum intervals needed int intervals = (maxCount - 1) * (n + 1) + maxCountTaskCount; //If the calculated intervals is less than the total number of tasks, return the total number of tasks return intervals < tasks.Length ? tasks.Length : intervals; } }