**Proposition 17.** *ps* ≥ *<sup>p</sup>*¯min{*y*}*.*

#### *9.1. The Activation of a Substitution Job*

Given that an IA(b2)/IA(b2-I) with job *y* after kernel *K*<sup>−</sup> arises, *s* ∈ B*<sup>K</sup>* is called a *substitution* job if *ds* > *dy*. Intuitively, job *s* is an emerging job for job *y* (the latter job surpasses the current *L*-boundary, and in this sense, it is a potential overflow job).

PROCEDURE ACTIVATE(*s*) that activates substitution job *s* has some additional features compared to the basic definition of Section 2, as we describe in this subsection (in the next subsection, we complete the description of Phase 2 by a subroutine that tries different substitution jobs to determine a "right" one).

Let *B*{(*s*)} be the bin from secondary block B*<sup>K</sup>* containing substitution job *s* (it follows that *s* was included as an x-job in bin *B*{(*s*)}). PROCEDURE ACTIVATE(*s*) reconstructs preschedules for the kernels in the current schedule <sup>Σ</sup>C(*δ*,*K*) between the kernel *<sup>K</sup>* with *<sup>B</sup>*−(*K* ) = *B*{(*s*)} (the kernel with its first surrounding bin *B*{(*s*)}) and kernel *K*−, including these two kernels, calling Phase 1 for each of these kernels (the kernel preschedules are reconstructed in their precedence order). This reconstruction leads to a new temporal configuration. PROCEDURE ACTIVATE(*s*) aims to verify if there exists a feasible *L*-preschedule for kernel *K* respecting this configuration. If it does not exist, PROCEDURE sl-SUBSTITUTION(*K*), described in the next subsection, tries another substitution job for kernel *K*, calling again Phase 1 for kernel *K*; each call creates a new temporary configuration and is carried out for a specially derived problem instance that depends on the selected substitution job.

For notational simplicity, we denote every newly constructed preschedule of kernel *K* by *PreS*(*K*); we distinguish preschedules constructed at different calls of Phase 1 just by referring to the call with the corresponding substitution job, and will normally use *PreS*(*K*) for the latest so far created preschedule for kernel *K*.

In the inductive case, the activation procedure for a substitution job *s* calls Phase 1 with a *non-empty* set SB of the substitution jobs, ones in the state of activation in the secondary block B by the corresponding call of Phase 1 (note that *s* ∈ SB). As already noted, the activation procedure may be called for different kernels which belong to the current secondary block, so that this block may contain a preschedule, already reconstructed by an earlier call of the activation procedure for another kernel from that block (set SB contains all the corresponding substitution jobs).

**Problem instances for the basic and inductive cases.** The problem instances for the basic and inductive cases are different, as we specify now. The problem instance PI(*current*, +*y*, [*s*]) of the *basic* case contains the jobs in schedule <sup>Σ</sup>C(*δ*,*K*) from all the bins between bin *<sup>B</sup>*{(*s*)} and bin *<sup>B</sup>*−(*K*−), including the jobs of bins *B*−(*K*−) and *B*{(*s*)} except job *s*, job *y* and all the y-jobs included before job *y* in preschedule *PreS*(*K*, *y*) of Pass 1 (the latter y-jobs are ones which were already included in bin *B*−(*K*) at Pass 1 when the IA(b2) with job *y* has occurred; note that no x-job for bin *B*−(*K*) is included in instance PI(*current*, +*y*, [*s*])).

The problem instance of the *inductive* case contains the same set of jobs as that in the basic case, and it also contains the substitution jobs from set SB. For the sake of simplicity, we denote that problem instance also by PI(*current*, +*y*, [*s*]).

**Successful and failure outcomes.** As already specified, the activation of job *s* consists of the rescheduling of preschedules of bins *B*{(*s*)}, ... , *B*−(*K*−) by a call of Phase 1 for instance PI(*current*, +*y*, [*s*]) in this precedence order (note that while rescheduling these bins only the jobs from that instance are considered). As we show at the end of this subsection in Lemma 11, all these bins will be successfully reconstructed at Phase 1.

PROCEDURE ACTIVATE(*s*) halts either with the successful outcome or with the failure outcome. For every successful outcome, the current call of Phase 2 (invoked for the IA(b2) with job *y*) completes and Phase 1 is repeatedly invoked from PROCEDURE MAIN for the construction of a new preschedule *PreS*(*K*) for kernel *K*. Intuitively, the difference between the configurations after this new and the previous calls of Phase 1 for kernel *K* is that, as a result of the new call, no job from problem instance PI(*current*, +*y*, [*s*]) may again surpass the *L*-boundary, and job *y* is already included in current secondary block in the new configuration. We omit a straightforward proof of the next proposition.

**Proposition 18.** *If there is a job from instance PI*(*current*, +*y*, [*s*]) *that the activation procedure could not include in any of the reconstructed bins B*{(*s*)}, ... , *B*−(*K*−)*, this job is a y-job for bin B*−(*K*) *(or it is a job from set* SB *in the inductive case). If a former y-job is of Type (a), then all such Type (a) y-jobs can be included in bin B*−(*K*) *during the construction of a new preschedule PreS*(*K*) *for kernel K at Phase 1.*

Note that, independently of the outcome, the activation procedure cannot include job *s* before any of the Type (b) y-jobs for bin *B*−(*K*) from instance PI(*current*, +*y*, [*s*]) in the basic case. However, as shown below, job *s* may be included ahead some of these Type (b) y-jobs at a later call of the activation procedure for a substitution job, different from job *s*, in the inductive case.

**Extension of Phase 1 for a call from the inductive case.** The activation procedure for the inductive case takes a special care on the jobs from set SB while invoking Phase 1 for instance PI(*current*, +*y*, [*s*]) (or instance PI(*current*, +*y*, [∅]) which we define below). In particular, when Phase 1 is called from the inductive case, two types of the x-jobs are distinguished during the (re)construction of a preschedule *PreS*(*K*¯), *<sup>K</sup>*¯ <sup>=</sup> *<sup>K</sup>* (one of the bins *<sup>B</sup>*{(*s*)}, ... , *<sup>B</sup>*−(*K*−)). The *Type (b)* x-jobs are ones which are also x-jobs for bin *B*−(*K*), and the rest of the x-jobs are *Type (a)* x-jobs. We observe that a Type (a) x-job for bin *B*−(*K*¯) will transform to a Type (b) y-job for bin *B*−(*K*) unless it is included in one of the preceding reconstructed bins *<sup>B</sup>*−(*K*¯), and that a substitution job from set SB is a Type (b) x-job for any bin *B*−(*K*¯).

Phase 1, when invoked from the inductive case, is extended with an additional, Pass 3, designed for scheduling the substitution jobs from set SB. Pass 3 uses the algorithm of Pass 2, DEF-heuristics, but with a different input, restricted solely to Type (b) x-jobs (hence, a former substitution job from SB may potentially be included at Pass 3). There is a respective modification in the input of Pass 2, which consists now of only Type (a) x-jobs (hence no substitution job from set SB will be included at Pass 2). Pass 3 is invoked after Pass 2, and Pass 2 is invoked after Pass 1, which remains unmodified while rescheduling each of the bins *B*{(*s*)},..., *B*−(*K*−).

Once (in both basic and inductive cases) preschedules of bins *B*{(*s*)}, ... , *B*−(*K*−) are reconstructed (Lemma 11), Phase 1 continues with the reconstruction of preschedule *PreS*(*K*) as follows.

• (A) If there remains no unscheduled job from instance PI(*current*, +*y*, [*s*]) (except possibly jobs from set SB in the inductive case), i.e., all these jobs are included in one of the reconstructed bins *B*{(*s*)},..., *B*−(*K*−), the activation procedure halts with the successful outcome.

If there is a job from instance PI(*current*, +*y*, [*s*]) that could not have been included in any of the reconstructed bins *<sup>B</sup>*{(*s*)}, ... , *<sup>B</sup>*−(*K*−) (excluding jobs from set SB in the inductive case), then it is a y-job for bin *<sup>B</sup>*−(*K*) (and it might also be a job from set SB in the inductive case). PROCEDURE ACTIVATE(*s*) proceeds as described below.


(C1) If during the construction of preschedule *PreS*(*K*) at Pass 1 an iteration is reached at which all the Type (b) y-jobs from instance PI(*current*, +*y*, [*s*]) are included, then the outcome of the activation of job *s* is again successful and Phase 1 continues with the construction of (a new) preschedule *PreS*(*K*) for kernel *K* by considering all the available jobs (including job *s*) without any further restriction.

(C2) If the above iteration during the construction of preschedule *PreS*(*K*) does not occur, then either (C2.1) a new kernel *K* including the corresponding type (a) y-job(s) arises or (C2.2) an IA(b2) with a Type (b) y-job occurs (see Proposition 12).

In Case (C2.1), Step 2 of Pass 1 returns kernel *K* and calls PROCEDURE MAIN to update the current configuration (see the description of Pass 1 in Section 7.1).

In Case (C2.2), PROCEDURE ACTIVATE(*s*) completes with the failure outcome (then PROCEDURE sl-SUBSTITUTION(*K*), described in the next subsection, looks for another substitution job *s* and calls repeatedly PROCEDURE ACTIVATE(*s* )).

This completes the description of PROCEDURE ACTIVATE(*s*). In the next subsection, we describe how we select a substitution job in the basic and inductive cases completing the description of Phase 2.

**Lemma 11.** *PROCEDURE ACTIVATE*(*s*) *creates an L-preschedule, for every reconstructed bin B*{(*s*)},..., *B*−(*K*−) *with the cost of Phase 1.*

**Proof.** In this proof, we refer to a call of PROCEDURE ACTIVATE(*s*) from the condition of the lemma as the current call of that procedure; note that, for the inductive case, there should have been performed earlier calls of the same procedure within the current secondary block. In particular, prior to the current call of PROCEDURE ACTIVATE(*s*), every bin *<sup>B</sup>*−(*K*¯) ∈ {*B*{(*s*)}, ... , *<sup>B</sup>*−(*K*−)} was (re)constructed directly at Phase 1 (one or more times). The current call reconstructs bin *B*−(*K*¯) (preschedule *PreS*(*K*¯)) once again. Recall also that problem instance PI(*current*, +*y*, [*s*]) contains additional job *y* and the Type (b) y-jobs preceding that job by the construction of the preschedule for kernel *K* at Pass 1 (these jobs were included prior to the occurrence of an IA(b2) with job *y*). All these Type (b) y-jobs for bin *B*−(*K*) become x-jobs for a bin *B*−(*K*¯) after the current call of PROCEDURE ACTIVATE(*s*).

Again, the activation procedure calls Phase 1, and by the construction of Phase 1, it will suffice to show that during the reconstruction of any of the bins *B*−(*K*¯), there will occur no Type (b) y-job that cannot be included in the newly created preschedule *PreS*(*K*¯) (note that no such Type (a) y-job may arise). Let us now distinguish two kinds of Type (b) y-jobs for bin *B*−(*K*¯): a Type (b) y-job that was also a Type (b) y-job during the previous (re)construction of preschedule *PreS*(*K*¯), and a newly arisen Type (b) y-job for bin *B*−(*K*¯), i.e., one that was earlier included as an x-job in a preceding preschedule *PreS*(*K* ) but which turned out to be a Type (b) y-job during the current construction of preschedule *PreS*(*K*¯).

The lemma is obviously true if there exists no latter kind of a y-job for bin *B*−(*K*¯). To the contrary, suppose job *x* was scheduled in bin *B*−(*K* ) (preceding bin *B*−(*K*¯)) as an x-job, but it was forced to be rescheduled to (a later) bin *B*−(*K*¯) as an y-job during the current call of PROCEDURE ACTIVATE(*s*). Then, during the current construction of preschedule *PreS*(*K* ) (the last call of PROCEDURE ACTIVATE(*s*) that has invoked Phase 1) a new x-job *z* was included before job *x* was considered at Pass 2 of Phase 1. By DEF-heuristics (Pass 2), this may only occur if a job scheduled in bin *B*−(*K* ) at the previous call is not considered at the current call during the construction of that bin (the preschedule *PreS*(*K* )). Let *N* be the set consisting of all such jobs. By the definition of instance PI(*current*, +*y*, [*s*]) and the activation procedure, a job in set *N* may be job *s* or a job which was left-shifted within the time intervals liberated by job *s* or by other left-shifted job(s).

Thus, job *z* has now occupied the time intervals within which job *x* and job(s) in set *N* were scheduled. *pz* ≥ 2*px*, as otherwise job *x* would have been considered and included in bin *B*−(*K* ) ahead of job *z* by DEF-heuristics (recall that the smallest job processing time, larger than *px* is 2*px*). Then, *p*(*N*) < *px* is not possible, since otherwise *p*(*N*) + *px* < 2*px* ≤ *pz* and the length of the released time intervals would not be sufficient to include job *z* in bin *B*−(*K* ) (hence, job *z* would not push out job *x*). If *p*(*N*) = *px*, because of the divisibility of job processing times and by DEF-heuristics, job *z* may only push out job *x* if *pz* = 2*px* = 2*p*(*N*). Then, *pz* is greater than the processing time of any job in set *N*. However, in this case, job *z* would have been included at the previous call in bin *B*−(*K*¯) ahead of job *x* and the jobs in set *N* since it is longer than any of these jobs, a contradiction.

If now at the current call *p*(*N*) > *px*, a job can be included ahead of job *z* in preschedule *PreS*(*K* ) within the time intervals earlier occupied by the jobs in set *N*. Let *p* , *p* ≤ *p*(*N*), be the length of the remaining total idle-time intervals. If *pz* ≤ *p* , then job *z* cannot push out job *z* since it fits within the remaining idle-time interval. If *pz* > *p* , then *pz* must be no smaller than the smallest power of 2 greater than *px* + *p* . Hence, job *z* cannot fit within the intervals of the total length of *px* + *p* , and, again, it cannot pull out job *x*.

We showed that job *z* cannot exist, hence job *x* does not exist and PROCEDURE ACTIVATE(s) creates an *L*-preschedule for the bins *B*{(*s*)}, ... , *B*−(*K*−). The cost of the procedure is the same as that of Phase 1 since the cost of the creation of problem instance PI(*current*, +*y*, [*s*]) is obviously absorbed by the cost of Phase 1.

#### *9.2. Selecting a Substitution Job*

Now, we describe PROCEDURE sl-SUBSTITUTION(*K*) that repeatedly activates different substitution jobs for an IA(b2) occurred at Phase 1 (using PROCEDURE ACTIVATE(*s*)) to determine one for which PROCEDURE ACTIVATE(*s*) completes with the successful outcome (whenever there exists such a substitution job). From here on, we refer to the original precedence order of the substitution jobs in the current secondary block B*<sup>K</sup>* (their precedence order corresponding to the last configuration in which none of them were activated).

**Lemma 12.** *Suppose an IA(b2)/IA(b2-I) with job y arises and s and s are the substitution jobs such that job s preceded job s . Then, if the outcome of activation of job s is the failure then outcome of activation of job s will also be the failure.*

**Proof.** Let *j* be any candidate job to be rescheduled before kernel *K*−, i.e., *j* = *y* or *j* is any of the Type (b) y-jobs included after kernel *K*− before the above IA(b2)/IA(b2-I) with job *y* has occurred (see Proposition 16). Job *j* is released either: (1) before the (former) execution interval of job *s* ; or (2) within or after that interval. In Case (1), job *j* can immediately be included in bin *B*{(*s* )}. Moreover, as *ps* > *pj*, if *j* cannot be included in bin *B*{(*s* )}, it can also not be included in any other bin before kernel *K*<sup>−</sup> (one preceding bin *B*{(*s* )}). In Case (2), job *j* cannot be included before kernel *K*<sup>−</sup> unless some jobs from bin *B*{(*s* )} and the following bins are left-shifted within the idle-time interval released by job *s* (releasing, in turn, the idle-time within which job *j* may be included). Again, since *ps* > *pj*, job *j* will fit within the idle-time interval released by job *s* , given that all the intermediate jobs are "sufficiently" left-shifted. Since job *s* succeeds job *s*, the activation of job *s* will left-shift these jobs

no-less than the activation of job *s* (being a substitution job, *s* is "long enough"). The lemma now obviously follows.

**Determining the sl-substitution job.** We use the above lemma for the selection of a right substitution job. Let us call the shortest latest scheduled substitution job such that the outcome of its activation is successful, the *sl-substitution* job for job *y*. We show in Lemma 16 that, if there exists no sl-substitution job, there exists no *L*-schedule.

Our procedure for determining the sl-substitution job is easy to describe. PROCEDURE sl-SUBSTITUTION(*K*) (invoked for an IA(b2) with a Type (b) y-job from Phase 1 during the construction of preschedule *PreS*(*K*)) finds the sl-substitution job or otherwise returns the failure outcome. Iteratively, it calls PROCEDURE ACTIVATE(*s*) for the next substitution job *s* (a candidate for the sl-substitution job) until PROCEDURE ACTIVATE(*s*) delivers a successful outcome or all the candidate jobs (which may potentially be the sl-substitution job) are considered.

The order in which the candidate substitution jobs are considered is dictated by Lemma 12. Recall from Proposition 17 that a substitution job is at least as long as *<sup>p</sup>*¯min{*y*}. Let *<sup>p</sup>*¯ ≥ *<sup>p</sup>*¯min{*y*} be the minimum processing time no smaller than *<sup>p</sup>*¯min{*y*} of any yet unconsidered substitution job. PROCEDURE sl-SUBSTITUTION(*K*), iteratively, among all yet unconsidered substitution jobs with processing time *p*¯ determines the latest scheduled substitution job *s* and calls PROCEDURE ACTIVATE(*s*) (see Lemma 12). If the outcome of PROCEDURE ACTIVATE(*s*) is successful, the outcome of PROCEDURE sl-SUBSTITUTION(*K*) is also successful and it returns job *s* (*s* is the sl-substitution job). Otherwise, if there exits the sl-substitution job, it is longer than job *s*. *p*¯ is set to the next smallest processing time larger than the current *p*¯, *s* becomes the latest scheduled substitution job with the processing time *p*¯ and PROCEDURE ACTIVATE(*s*) is called again. The procedure continues in this fashion as long as the latest outcome is the failure and *p*¯ can be increased (i.e., a substitution job with the processing time greater than that of the latest considered one exists). Otherwise, PROCEDURE sl-SUBSTITUTION(*K*) halts with the failure outcome.

Let *μ* be the number of non-kernel jobs in the current secondary block B*K*.

**Lemma 13.** *PROCEDURE sl-SUBSTITUTION finds the sl-substitution job or establishes that it does not exist by verifying at most* log *p*max *substitution jobs in time O*(log *p*max*μ*<sup>2</sup> log *μ*)*.*

**Proof.** The preprocessing step of PROCEDURE sl-SUBSTITUTION creates a list in which the substitution jobs are sorted in non-decreasing order of their processing times, whereas the jobs of the same processing time are included into the inverse precedence order of these jobs in that list. The preprocessing step takes time *O*(*μ* log *μ*).

Since the processing time of every next tried substitution job is larger than that of the previous one, the procedure works on log *p*max iterations (assuming that the processing times of the substitution jobs are powers of 2). By Lemma 12, among all the candidate substitution jobs with the same processing time, it suffices to consider only the latest scheduled one. For the failure outcome, by the same lemma, it suffices to consider the latest scheduled substitution job with the next smallest processing time (given that the procedure starts with the latest scheduled substitution job with the smallest processing time).

At every iteration, the corresponding bins from the current secondary block B*<sup>K</sup>* are rebuilt at Phase 1. Applying Theorem 2 and the fact that different bins have no common jobs, we easily obtain that the cost of the reconstruction of all the bins at that iteration is *O*(*μ*<sup>2</sup> log *μ*) and hence the total cost is *O*(*μ* log *μ* + log *p*max*μ*<sup>2</sup> log *μ*) = *O*(log *p*max*μ*<sup>2</sup> log *μ*).

#### **10. More Examples**

Before we prove the correctness of our algorithm for problem 1|*pj* : *divisible*,*rj*|*L*max, we give final illustrations using the problem instances of Examples 1 and 2 and one additional problem instance, for which an IA(b2) arises. Recall that Figures 5 and 9 represent optimal solutions for the former two problem instances.

For the problem instance of Example 1, in the schedule of Figure 6 the collapsing of kernel *K* is complete and the decomposition procedure identifies the atomic kernel *K*∗; hence, the corresponding two bins are determined. The atomic kernel *K*<sup>∗</sup> consists of Job 1 with the lateness −1 = *L*<sup>∗</sup> max. The binary search is carried out within the interval [0, 13) (Δ = 16 − 3 = 13). For *δ* = 7, the *Lδ*-boundary is −1 + 7 = 6. At Phase 1, bins *B*<sup>1</sup> and *B*<sup>2</sup> are scheduled as depicted in the schedule of Figure 5 (in bin 1 only a single x-Job 2 at Pass 2 can be included, whereas bin *B*<sup>2</sup> is packed at Pass 1 with two y-Jobs 3 and l). Hence, the *L*-schedule of Figure 5 for *L* = 6 is successfully created. For the next *δ* = 4, the *Lδ*-boundary is −1 + 4 = 3. Bin *B*<sup>1</sup> is scheduled similarly at the iteration with *δ* = 7; while scheduling bin *B*<sup>2</sup> at Phase 1, an IA(b2) with y-Job 3 occurs (since its lateness results to be greater than 3), but there exists no substitution job. Hence, there exists no *Lδ*-schedule for *δ* = 4, *L* = 3. Phase 1 will complete with the similar outcome for the iteration in the binary search with *δ* = 6, and the algorithm halts with the earlier obtained feasible solution for *δ* = 7.

For the problem instance of Example 2, the schedule of Figure 8 represents the result of the decomposition of both arisen kernels *K*<sup>1</sup> and *K*<sup>2</sup> (kernel *K*<sup>2</sup> arises once the decomposition of kernel *K*<sup>1</sup> is complete and bin *B*<sup>1</sup> gets scheduled). We have *L*max(*K*1<sup>∗</sup> ) = *L*3(*σl*,2) = 19 − 20 = −1, whereas Δ = 32 − 3 = 29. For *δ* = 0, bin *B*<sup>1</sup> may contain only Job 1. Once bin *B*<sup>1</sup> is scheduled, the second kernel *K*<sup>2</sup> arises. The result of its collapsing is reflected in Figure 8. We have *L*<sup>∗</sup> max = *L*max(*K*2<sup>∗</sup> ) = *L*6(*σl*,2,4) = <sup>62</sup> <sup>−</sup> <sup>58</sup> <sup>=</sup> 4. Then, *<sup>δ</sup>*(*K*1<sup>∗</sup> ) = 5 (while *δ*(*K*2<sup>∗</sup> ) = 0), and an extra delay of 5 is now allowed for kernel *<sup>K</sup>*1. Note that the current secondary block <sup>B</sup>*K*<sup>2</sup> includes all the three bins. For *<sup>δ</sup>* <sup>=</sup> 0, bin *<sup>B</sup>*<sup>1</sup> is newly rescheduled and at Pass 2 of Phase 1 an x-Job 7 is now included in that bin (due to the allowable extra delay for kernel *K*1<sup>∗</sup> ). No other job besides Job *l* can be included in bin *B*2, and the last bin *B*<sup>3</sup> is formed by Job 4. A complete *L*-schedule (with *L<sup>δ</sup>* = *L*<sup>∗</sup> max + *δ* = 4 + 0 = 4) with the objective value equal to a lower bound 4 is successfully generated (see Figure 9).

**Example 4.** *In this example, we modify the problem instance of Example 2. The set of jobs is augmented with one additional Job 8, and the parameters of Jobs 4 and 7 are modified as follows: r*<sup>4</sup> = 0, *p*<sup>4</sup> = 8, *d*<sup>4</sup> = 66*, r*<sup>7</sup> = 0, *p*<sup>7</sup> = 4, *d*<sup>7</sup> = 60*,*

*r*<sup>8</sup> = 0, *p*<sup>8</sup> = 4, *d*<sup>8</sup> = 63*.*

Figure 10 represents the last step in the decomposition of kernel *K*1, which is the same as for the problem instance of Example 2 (the schedules represented in Figures 10–13 have different scaling due to the differences in their lengths). This decomposition defines the two bins surrounding atomic kernel *K*1<sup>∗</sup> . The binary search is invoked for *δ* = 0; since *K*1<sup>∗</sup> is the only detected kernel so far, *δ*(*K*1<sup>∗</sup> ) = 0 and *<sup>L</sup><sup>δ</sup>* <sup>=</sup> *<sup>L</sup>*3(*S*∗[*K*1]) = <sup>−</sup>1. The first bin is successfully packed with an additional external x-Job 7 at Pass 2 of Phase 1 (since there exists no y-job, Pass 1 is not invoked). PROCEDURE MAIN proceeds by applying ED-heuristics from Time 21 during which the second kernel *K*<sup>2</sup> arises. Figure 11 represents the resultant partial schedule with the first packing of bin *B*<sup>1</sup> and kernel *K*2. Figure 12 represents the result of the full decomposition of kernel *K*<sup>2</sup> (which is again the same as for the problem instance of Example 2). Now, *δ*(*K*2<sup>∗</sup> ) = 0 and *δ*(*K*1<sup>∗</sup> ) = 5. Bin *B*<sup>1</sup> is repacked, in which a longer x-Job 4 can now be included, and bin *B*<sup>2</sup> at Phase 1 is packed (at Pass 1 an y-Job 2, and at Pass 2 an x-Job *l* is included in that bin, see Figure 12). PROCEDURE MAIN is resumed to expand the current partial schedule, but now an IA(b2) with the earliest included Job 7 arises (as its resultant lateness is 66 − 60 = 6 > 4). Job 4 from bin *B*<sup>1</sup> is the sl-substitution job. The result of its activation is reflected in Figure 13: bin *B*<sup>1</sup> is repacked now with x-Jobs 7 and 8, bin *B*<sup>2</sup> remains the same, and the last Job 4 is included in bin *B*<sup>3</sup> at Phase 0, yielding a complete *SL*<sup>0</sup> -schedule with the optimal objective value 4 (both Jobs 6 and 4 realize this optimal value).

$$\frac{\frac{1}{19} \cdot \frac{1}{19} \cdot \frac{1}{19} \cdot \frac{1}{19} \cdot \frac{1}{19} \cdot \frac{1}{19} \cdot \frac{1}{19} \cdot \frac{1}{19} \cdot \frac{1}{19} \cdot \frac{1}{19} \cdot \frac{1}{19}}$$

**Figure 10.** Full decomposition of kernel *K*1.

**Figure 13.** An optimal schedule *S<sup>L</sup>* in which bins *B*1, *B*<sup>2</sup> and *B*<sup>3</sup> are successfully repacked.

#### **11. Correctness of the Framework for Jobs with Divisible Processing Times**

In Section 8, we show that, for an instance of 1|*pj* : *divisible*,*rj*|*L*max, the current secondary block is kept active at Phase 1 (Corollary 4). Now, we generalize this result proving a similar statement for a secondary block that gets reconstructed at Phase 2 (we cannot affirm a similar property for an instance of 1|*rj*|*L*max, a reason PROCEDURE MAIN may not provide an optimal solution to the general problem). For the commodity in the proof that we present here, we introduce a few new definitions.

First, we observe that a call of PROCEDURE ACTIVATE(*s*) may create the new, so-called *critical* gap(s) in a reconstructed preschedule in the current secondary block B*K*. To every critical gap in secondary block B*K*, the substitution job from set SB which activation has yielded that gap, corresponds. Denote by *CG*(*s*) the set of all the currently remaining (yet unused at that configuration) critical gaps yielded by the activation of a substitution job *s*; let |*CG*(*s*)| be the total length of these gaps.

A substitution job *<sup>s</sup>* ∈ SB is *stable* if |*CG*(*s*)| = 0. When a substitution job *<sup>s</sup>* is activated, the total length of the critical gaps arisen after its activation depends, in particular, on *ps*. For example, in the basic case, or in the inductive case if substitution jobs in SB are stable, the new critical gaps with the total length *ps* − *py* will arise, where *y* is the y-job for which *s* was activated.

If an activated substitution job *s* is non-stable and during a later call of PROCEDURE ACTIVATE(*s* ), *s* = *s*, some y-job within the interval of the gaps in *CG*(*s*) is included, |*CG*(*s*)| will be reduced. In this way, job *s* may eventually become stable.

For a substitution job *<sup>s</sup>* ∈ SB, we let *<sup>Y</sup>*(*s* ) be the set of all the newly included y-jobs in the reconstructed bins after a call of PROCEDURE ACTIVATE(*s* ) (see Lemma 11).

Suppose a call of PROCEDURE ACTIVATE(*s*) (succeeding an earlier call of PROCEDURE ACTIVATE(*s* )) includes job *s* before all the y-jobs from set *Y*(*s* ). Then, job *s* is said to be *inactivated*. The intuition behind this definition is that job *s* will not necessity remain in the state of activation for all jobs from set *Y*(*s* ) in the case that the activation of a new substitution job *s* gives a sufficient room for a proper accommodation of the jobs in set *Y*(*s* ) (this is rectified in more details in the proof below). At the same time, we may note that job *s* may not be included in any of the newly reconstructed bins and neither in bin *B*−(*K*) (then it eventually will be included within a succeeding bin of the secondary block B*K*).

**Lemma 14.** *At Phase 2, the current secondary block is kept compact given that for every occurrence of an IA(b2-I) the corresponding sl-substitution job exists.*

**Proof.** For the basic case, before the activation of the corresponding sl-substitution job, say *s*1, the critical block B*<sup>K</sup>* is compact by Corollary 4. Since *s*<sup>1</sup> is the sl-substitution job, block B*<sup>K</sup>* will remain compact after the above activation. We are brought to the inductive case if an IA(b2) repeatedly arises in the above block, the first occurrence of an IA(b2-I) with the number of the activated substitution jobs *k* = 1.

We proceed with the proof using the induction on the number of the activated substitution jobs. We now prove our claim for *k* = 2, in the case that the second substitution job *s*<sup>2</sup> is activated in the current secondary block. Consider the following possibilities. Originally, job *s*<sup>2</sup> either: (i) succeeded job *s*1; or (ii) preceded job *s*1.

In Case (i), if *ps*<sup>2</sup> ≥ 2*ps*<sup>1</sup> , all the y-jobs already included within *CG*(*s*1) together with jobs in set Λ*y*<sup>2</sup> can be feasibly scheduled within *CG*(*s*2) as *ps*<sup>2</sup> ≥ 2*p*(Λ*y*<sup>2</sup> ). Hence, after a call of PROCEDURE ACTIVATE(*s*2) job *s*<sup>1</sup> will be inactivated at Pass 3 (see Lemma 11). Hence, job *s*<sup>1</sup> becomes stable and we are left with a single substitution job *s*<sup>2</sup> in the state of activation.

In Case (ii), note that no job from set Λ*y*<sup>2</sup> was included within *CG*(*y*1) after a call of PROCEDURE ACTIVATE(*s*1). Hence, |*CG*(*s*1)| < *p*(Λ*y*<sup>2</sup> ). If job *s*<sup>2</sup> is long enough and all jobs in *Y*(*s*1) are released early enough and can fit within the newly released space by job *s*<sup>2</sup> after a call of PROCEDURE ACTIVATE(*s*2), once Pass 3 of the activation procedure completes, job *s*<sup>1</sup> will again become stable and we are again left with a single substitution job *s*<sup>2</sup> in the state of activation.

Since in the above considered cases, the only non-stable substitution job is *s*2, our claim follows from case *k* = 1 and the fact that *s*<sup>2</sup> is the sl-substitution job. It only remains to consider the cases when job *s*<sup>1</sup> remains in the state of the activation after a call of PROCEDURE ACTIVATE(*s*2), i.e., both substitution jobs *s*<sup>1</sup> and *s*<sup>2</sup> remain in the state of activation. This happens in Case (i) if *ps*<sup>2</sup> ≤ *ps*<sup>1</sup> (note that in this case *ps*<sup>2</sup> ≤ *p*(Λ*y*<sup>2</sup> ) also holds as otherwise job *s*2, instead of job *s*1, would have been selected as the sl-substitution job for job *y*1). Either jobs in set Λ*y*<sup>2</sup> are not released early enough to be included within *CG*(*s*1) or |*CG*(*s*1)| is not large enough. Hence, another substitution job needs to be activated to include jobs in Λ*y*<sup>2</sup> (see Lemma 15 below). Since *s*<sup>2</sup> is the sl-substitution job, |*CG*(*s*2)| is the minimal possible. The lemma holds if job *s*<sup>1</sup> again becomes stable. Otherwise, note that, since both *s*<sup>1</sup> and *s*<sup>2</sup> are the sl-substitution jobs, the only remaining possibility to be considered is when a single substitution job *s* with *ps* < *ps*<sup>1</sup> + *ps*<sup>2</sup> (instead of jobs *s*<sup>1</sup> and *s*2) is activated.

Consider the following sub-cases: (1) |*CG*(*s*1)| ≥ *p*(Λ*y*<sup>2</sup> ); and (2) |*CG*(*s*1)| < *p*(Λ*y*<sup>2</sup> ). In Case (1). jobs in Λ*y*<sup>2</sup> are not released early enough to be included within *CG*(*s*1) as otherwise they would have been included by an earlier call of PROCEDURE ACTIVATE(*s*1). Hence, no job preceding originally job *s*<sup>1</sup> can be beneficially activated. At the same time, any substitution job succeeded originally job *s*<sup>1</sup> is longer than *s*<sup>1</sup> (by the definition job *s*<sup>1</sup> and PROCEDURE sl-SUBSTITUTION). Then, *ps* ≥ 2*ps*<sup>1</sup> because of the divisibility of job processing times. In Case (2), *ps* ≥ 2*ps*<sup>1</sup> must also hold as otherwise all jobs in set *Y*(*s*1) together with jobs in set Λ*y*<sup>2</sup> would not fit within the time intervals that potentially might be liberated by a call of PROCEDURE ACTIVATE(*s*).

Hence, in both Cases (1) and (2) above, *ps* < *ps*<sup>1</sup> + *ps*<sup>2</sup> is not possible and hence the activation of job *s* will yield the critical gaps with a total length no less than our procedure yields, and the lemma follows. The proof for the Case (ii) when the jobs in *Y*(*s*1) do not fit within *CG*(*s*2) or they are not released early enough is quite similar to case (i) above (the roles of jobs *s*<sup>1</sup> and *s*<sup>2</sup> being interchanged).

For the inductive pass with *k* ≥ 3, let *sk* be the next activated sl-substitution job and let SB = {*s*1,...,*sk*−1} be the substitution jobs in the state of activation in the current critical block B). By the inductive assumption, block B was compact before job *sk* is activated. Now, we show that the block remains compact once job *sk* is activated. This follows if *sk*, as before, remains the only (non-stable) substitution job in the state of activation after a call of PROCEDURE ACTIVATE(*sk*). Otherwise, originally, job *sk*: (i) succeeded all the jobs {*s*1, ... ,*sk*−1}; (ii) preceded these jobs; or (iii) was scheduled in between their original positions. We use similar arguments as for *k* = 2. We give a scratch.

In Case (ii), note that the time intervals released by a call of PROCEDURE ACTIVATE(*sk*) will be available for the jobs from set *<sup>Y</sup>*(*s*1) ∪···∪ *<sup>Y</sup>*(*sk*−1) during the execution of the procedure at Pass 2 of Phase 1, and they may potentially be left-shifted to these intervals. Because of the mutual divisibility of processing times of these jobs and by the construction of Pass 2, the total length of the remaining idle-time intervals, if any, will be the minimal possible (this can be straightforwardly seen). It follows

that, at Pass 3, the corresponding jobs from SB will become inactivated and hence stable, whereas the rest of them are to stay in the state of activation, and our claim follows from the inductive assumption.

In Case (i), all jobs from *<sup>Y</sup>*(*s*1) ∪···∪ *<sup>Y</sup>*(*sk*−1) are released early enough to be included within the intervals newly released by a call of PROCEDURE ACTIVATE(*sk*). Again, because of the mutual divisibility of processing times of these jobs and by the construction of Pass 2, the remaining idle-time intervals, if any, will be the minimal possible, and at Pass 3 the corresponding substitution jobs will be inactivated.

The proof of Case (iii) merely combines those for Cases (i) and (ii): at Pass 2, the intervals released by a call of PROCEDURE ACTIVATE(*sk*) might be used by jobs from *<sup>Y</sup>*(*s*1) ∪···∪ *<sup>Y</sup>*(*sk*−1) preceding and also succeeding these intervals, and the corresponding jobs from {*s*1, ... ,*sk*−1} will again become stable.

**Lemma 15.** *Suppose an IA(b2)/IA(b2-I) with job y during the construction of preschedule PreS*(*K*) *arises and there exists an L-schedule SL. Then, a substitution job is scheduled after kernel K*<sup>−</sup> *in schedule SL. That is, there exists no L-schedule if there exists no substitution job.*

**Proof.** The lemma is a kind of reformulation of Proposition 16. For the basic case, before the activation of the sl-substitution job *s*1, the secondary block B*<sup>K</sup>* is compact by Corollary 4. Similar to in the proof of Proposition 16, we can see that the current starting time of job *y* cannot be reduced by any job rearrangement that leaves the same set of jobs scheduled before job *y*. Hence, some emerging x-job *s* from one of the bins from the secondary block B*<sup>K</sup>* pushing job *y* is included behind job *y* in schedule *S<sup>L</sup>* (recall that *ds* > *dy* must hold as, otherwise, once rescheduled after kernel *K*−, job *s* will surpass the *L*-boundary or will force another y-job to surpass it). Job *s* cannot be from bin *B*−(*K*) since no x-job can be included ahead of job *y* during the construction of *PreS*(*K*) as job *y* is released from the beginning of that construction (and it would have been included at Pass 1 of Phase 1 before any x-job is considered at Pass 2). Therefore, job *s* belongs to one of the bins preceding bin *B*−(*K*) in block B*K*. The proof for the inductive case is similar except that it uses Lemma 14 instead of Corollary 4.

#### **Lemma 16.** *If there exists no sl-substitution job, then no L-schedule exists.*

**Proof.** If there exists no substitution job at all, then the statement follows from Lemma 15. Otherwise, the outcome of the activation of every tried substitution is the failure. We claim that there exists no *L*-preschedule that contains the jobs from problem instance PI(*current*, +*y*, [*s*]) together with all the jobs from all the (intermediate) kernels between the bins *B*{(*s*)} and *B*−(*K*−). Let *s* be the earliest tried substitution job by PROCEDURE sl-SUBSTITUTION(*K*). If job *s* becomes non-stable after a call of PROCEDURE ACTIVATE(*s*), then, due to the failure outcome, it must be the case that the corresponding y-job(s) (see Proposition 16) cannot be left-shifted within the time intervals liberated by job *s* (because of their release times). Hence, neither they can be left-shifted by activation of any substitution job preceding job *s* (Lemma 12). Otherwise, it must have been stable once activated, but the interval released by job *s* is not long enough (again, due to the failure outcome). Hence, only another longer substitution job may be of a potential benefit, whereas the latest scheduled one, again, provides the maximum potential left-shift for the above y-job(s). We continue applying this argument to every next tried substitution job. Our claim and hence the lemma follow due to the failure outcome for the latest tried (the longest) substitution job.

Now, we immediately obtain the following corollary that already shows the correctness of PROCEDURE MAIN for divisible processing times:

**Corollary 5.** *For every trial δ, PROCEDURE MAIN generates an Lδ-schedule if the outcome of every call of PROCEDURE sl-SUBSTITUTION*(*K*) *for an IA(b2) is successful (or no IA(b2) arises at all); otherwise (there exists no sl-substitution job for some IA(b2)), no Lδ-schedule exists.*

**Theorem 6.** *PROCEDURE MAIN optimally solves problem* 1|*pj* : *divisible*,*rj*|*L*max *in time O*(*n*<sup>3</sup> log *n* log2 *p*max)*.*

**Proof.** The soundness part immediately follows from Corollary 5 and the definition of the binary search in Section 5 (see Proposition 8). We show the time complexity. Due to Theorem 3, it remains to estimate an additional cost yielded by Phase 2 for instances of alternative (b2). Recall from Theorem 2 that, for every arisen kernel *K*, the cost of the generation of *Lδ*-augmented schedule *SL<sup>δ</sup>* [*K*] for a given *δ* is *O*(*ν*<sup>2</sup> log *ν*), where *ν* is the total number of jobs in bin *B*−(*K*). Recall also that this cost includes the cost of all the embedded recursive calls for all the kernels which may arise within bin *B*−(*K*). Similar to in the proof of Theorem 3, it suffices to distinguish the calls of PROCEDURE AUGMENTED(*K*, *δ*) and PROCEDURE AUGMENTED(*M*, *δ*) for two distinct kernels *K* and *M* such that bins *B*−(*K*) and *B*−(*M*) have no jobs in common. Now, we count the number of such calls of PROCEDURE AUGMENTED(*K*, *δ*) from Phase 2 by PROCEDURE sl-SUBSTITUTION(*K*). The number of times, an IA(b2) at Phase 1 may arise is bounded by *υ*1, the number of Type (b) y-jobs (note that any Type (b) y-job may yield at most one IA(b2)). Hence, for any bin *B*−(*K*), PROCEDURE AUGMENTED(*K*, *δ*) may be called less than *υ*<sup>1</sup> times for different instances of Alternative (b2), whereas for the same IA(b2) no more than *p*max different substitution jobs might be tried (Lemma 13). Hence, the total number of calls of PROCEDURE AUGMENTED(*K*, *δ*) is bounded above by *O*(*υ*<sup>1</sup> + *p*max), which easily yields the overall bound *O*(*n*<sup>3</sup> log *n* log2 *p*max).

#### **12. Possible Extensions and Applications**

We describe our framework for the single-machine environment and with a due-date oriented objective function *L*max. It might be a subject of a future research to adopt and extend the proposed framework for other machine environments with this or another due-date oriented objective function. Both the recurrence substructure properties and the schedule partitioning into kernel and bin intervals can be extended for the identical machine environment and shop scheduling problems with job due-dates. Less straightforward would be its adaptation for the uniform machine environment, and, unlikely, the approach can be extended to the unrelated machine environment.

The framework can obviously be converted to a powerful heuristic algorithm, as well as to an exact implicit enumeration scheme for a general setting with arbitrary job processing times. For both heuristic and enumerative approaches, it will clearly suffice to augment the framework with an additional search procedure invoked for the case when the condition of Theorem 3 is not satisfied.

Based on the constructed framework, we have obtained an exact polynomial-time algorithm for problem 1|*pj* : *divisible*,*rj*|*L*max. A natural question is whether, besides the scheduling and bin packing problems ([4]), there are other NP-hard combinatorial optimization problems for which restrictions with divisible item sizes are polynomially solvable (the properties of mutually divisible numbers exploited in reference [4] and here could obviously be helpful).

Finally, we argue that scheduling problems with divisible job processing times may naturally arise in practice. As an example, consider the problem of distribution of the CPU time and the computer memory, the basic functions of the operating systems. In Linux operating systems buddy memory allocation is used, in which memory blocks of sizes of powers of 2 are allocated. To a request for memory of size *<sup>K</sup>*, the system allocates a block of size 2*<sup>k</sup>* where 2*k*−<sup>1</sup> <sup>&</sup>lt; *<sup>K</sup>* <sup>≤</sup> <sup>2</sup>*<sup>k</sup>* (if currently there is no available block of size 2*k*, it splits the shortest available block of size 2*k*+<sup>1</sup> or more). In buddy systems, memory allocation and deallocation operations are naturally simplified, as an *O*(*n*) time search is reduced to *O*(log *n*) time using binary tree representation for blocks.

A similar "buddy" approach for the CPU time sharing in operating systems would assume the "rounding" of the arriving requests with arbitrary processing times within the allowable patterns of processing times—the powers of 2. In the CPU time sharing, the system must decide which of the arriving requests to assign to the processor and when. The request may arrive over time or, in the case of the scheduled maintenance and other scheduled computer services (for example, operating system updates), the arrival time of the requests and their processing times are known in advance. The latter scenario fits into our model. One may think on the rounding of a processing time of a request up or down to a closer power of 2. Alternatively, to avoid unnecessary waste of the processor time, one may always round down and process the remaining small part in a parallel or sequential manner immediately upon the completion of the main part or later on. Possible efficient and practical strategies for "completing" the solution with divisible processing times in a single-processor or multiprocessor environment deserves an independent study.

The "buddy" approach for the CPU time sharing in operating systems is justified by our results, as we show that the scheduling problems with mutually divisible processing times can be solved essentially more efficiently than with arbitrary job processing times. The degree of the "waste" during the rounding of the memory blocks and processing requirements is somewhat similar and comparable in both the memory allocation and the CPU time sharing methods. In the case of the memory allocation we may waste an extra memory, and in the case of the time sharing we waste an extra time (which would influence on the quality of the solution of course). It is important and not trivial how an input with arbitrary job processing times can be converted to an input with divisible processing times, and how close the obtained optimal solution for the instance with divisible times will be from an optimal solution for the original instance. This interesting topic can be a subject of a future independent study.

**Funding:** This research received no external funding.

**Conflicts of Interest:** The author declares no conflict of interest.

#### **References**


c 2019 by the author. Licensee MDPI, Basel, Switzerland. This article is an open access article distributed under the terms and conditions of the Creative Commons Attribution (CC BY) license (http://creativecommons.org/licenses/by/4.0/).
