I have several groups of tasks, each group is a chain of tasks, groups are independent of each other. Tasks within a group can only be processed in the order which is determined by the chain of that group.
Each task has an ID and a cost. Tasks are atomic, they can only be finished at once by investing time units equal to their cost into them (it's not possible to solve half of a task). At the beginning of each step there are m
time units available.
I want to check if it is possible to finish all tasks within a given number of steps d
.
Here are some pictures to clarify the situation, each task is a 2-tuple (ID, Cost), the tasks in the chains can only be solved from left to right.
Here is a graphic example of 6 tasks arranged into 3 groups:
Let's say that m = 5
(there are 5 time units available during each step) and that d = 4
(we want to check if all tasks can be finished within 4 steps).
A possible soulution would be:
Another possible solution would be:
An invalid solution would be (it finishes all tasks in 5 steps, we said that the limit is 4):
My question:
For given:
- tasks which are arranged into groups
- a number of time units
m
available at each step - and a number of steps
d
which are allowed
determine if it is possible to solve all tasks within d
steps, if so then output a possible sequence (task ID's) in which the tasks can be solved such that <= d
steps are done.
My current approach:
I try to find a solution by backtracking. I create a list of deques to model the groups, then I look at the set A (all the tasks which can be solved during the current step, the leftmost element of each group) and find all subsets B (subsets of A whose sum of costs is <= d
and to which no other task can be added such that the sum of costs stays <= d
). The subsets of set B represent the tasks which I consider solving during the current step, now each subset represents a choice, I do a recursive call for each of them (to explore each choice) where I pass the list of deques without elements in B (I remove them from the deques because from now on I consider them solved in this branch of recursion). The recursive calls stop once the depth of recursion is > d
(the number of allowed steps is exceeded) or a solution is found (the list of deques is empty, all tasks have ben solved within <= d
steps).
PseudoJavaish code:
// sequence[1] = j means that task 1 is done at step j
// the param steps is used to track the depth of recursion
findValidSequence (ArrayList<Deque> groups, int[] sequence, int steps) {
if (steps > d) // stop this branch since it exceeds the step limit d
if (groups.isEmpty()) // 0 tasks left, a solution is found, output sequence
Set A = getAllTasksSolvableDuringCurrentStep();
Set B = determineAllTheOptionsForTheNextStep(A);
// make a recursive call for each option to check if any of them leads to a valid sequence
for (each element in B)
findValidSequence(groups.remove(B), sequence.setSolvedTasks(B), steps+1);
}
I get lost trying to implement this correctly, what do you think of my approach, how would you solve this problem?
Note:
The problem is pretty general as lots of scheduling problems (m
machines and n
precedence constrained jobs) can be reduced to such a problem.