**The problem: **Scheduling to minimize weighted completion time. This is problem SS13 in the appendix.

**The description: **Given a set T of tasks where each task t has a length l(t) and weight w(t), a set of m processors, and a bound K. Can we find an m-processor schedule for all tasks where each project’s “completion penalty” is its weight multiplied by it’s completion time, and the total completion penalty for all tasks is K or less?

**Example: **Suppose I had 2 processors and 4 tasks:

Task | Length | Weight |

1 | 5 | 4 |

2 | 1 | 6 |

3 | 3 | 3 |

4 | 3 | 3 |

Scheduling greedily by weight would put task 1 and 2 on one processor, then tasks 3 and 4 on the second processor after task 2 finishes. This has a total penalty of 20 for task 1 + 6 for task 2 + 12 for task 3 + 21 for task 4 = 59

Scheduling task 2 then 1 on processor 1 and tasks 3 and 4 on processor 2 gives a total penalty of 57.

**Reduction: **The paper by Lenstra, Rinnooy Kan, and Brucker was hard for me to follow because they are discussing lots of different scheduling variants in the same paper, and do it by parameterizing the kinds of inputs. I’m pretty sure our problem is Theorem 3b in the paper, which reduces from Partition. So we start with a set T of elements and build 1 task for each element in T. The processing time and weight of the task are the size of the element in T. K will be the sum of the products of all pairs of elements – 1/4 of the sum of all of the elements squared. We’ll have 2 processors.

They claim that since the processing times and lengths are all the same for each task, if you are given a set of these tasks on one processor, the order doesn’t matter, and we get the same total weight no matter what. It took me a while to buy that, but it comes down to the fact that each task is costing one penalty unit per time step no matter where it is scheduled.

Given a subset T of S, define c to be the amount the sum of the elements in S is “off” of the partition. (The difference between the sum of the elements in T and half of the sum of everything in S). When c=0, T is a partition of S.

The completion penalty of a processor is the completion penalty of all of the tasks (remember, the order doesn’t matter) – the sum of all of the weights of tasks on that processor * the sum of all of the weights not on that processor. Which is K+c^{2}. Which means we have a solution to the scheduling problem if and only if c is 0, which is exactly when we have a partition.

**Difficulty: **6. I’m not sure how much I’d trust students to be able to come up with the algebra themselves. Make if you tell them to set up the problem with lengths and weights equal, and then make them prove that in that case, the order doesn’t matter, then it becomes more tractable for them.

**The problem: **Preemptive Scheduling. This is problem SS12 in the appendix.

**The description: ** Given a set T of tasks arranged in a partial order, each task t has a positive length l(t). We have a set of m processors and an overall deadline D. Can we create a preemptive schedule for T that obeys the precedence constraints and meets the overall deadline? A preemptive schedule allows tasks to be partially run on a processor and then removed, and we can finish it at some later time.

**Example: **Here is a simple partial order:

The number of the task is its length. So task a takes 3 time units, for example.

A two-processor preemptive schedule could be something like: P1: (1,1,1,2,2,4) and P2: (4,4,4,3,3,3), getting both tasks done at time6.

If we did not allow preemption, we would not be able to fit task 4 along with task 1, so we couldn’t do anything on P2 while P1 was running task 1.

**Reduction: **The paper by Ullman which we used for Precedence Constrained Scheduling has the reduction. He basically notes that Precedence Constrained Scheduling is a special instance of Preemptive Scheduling where all lengths of all jobs are 1. In that case, no preemption is possible. So the reduction is basically “use the exact same problem”.

**Difficulty:** 2. I usually reserve Difficulty 1 for “this is the same problem”, but here, there is a little bit of insight to convince yourself that you *can* interpret the problem this way.

**The problem: **Scheduling with individual deadlines. This is problem SS11 in the appendix.

**Description: **Given a set T of tasks, each of length 1, arranged in a partial order, a set of m processors, and a deadline d(t) for each task t, can we schedule all tasks to meet all deadlines?

**Example: **Here’s a precedence graph:

Suppose the deadlines are:

Task | Deadline |

1 | 1 |

2 | 3 |

3 | 2 |

4 | 1 |

5 | 2 |

6 | 3 |

If we have 2 processors, then we can schedule all tasks by doing each task exactly as its deadline is coming up. If we add one more requirement in the partial order from 3 to 5, then these tasks cannot all be finished by the deadline.

**Reduction (easy version): **When I read this problem, it seemed very similar to Precedence Constrained Scheduling. The difference is that in that problem, we’re given a single deadline D that all tasks must be completed by, but in this problem, each task can have its own deadline.

But this still leads to a pretty easy reduction from PCS: We take our PCS instance, use the exact same tasks, use the same number of processors, and make each task’s individual deadline be D. So our Individual Deadline instance is feasible exactly when all tasks are finished by time D.

This feels too easy. Maybe I’m misreading one of the problems. So, to be safe..

**Reduction** **(Brucker, Garey, and Johnson version):**

The reduction in the paper goes from Vertex Cover. So we’re given an undirected graph G=(V,E), and a K. Define define v = |V|, e= |E|, a= v+ e(e+1). Define m = a + e+ v. This will be the number of processors. Define D = 3e+3. This is the largest deadline for any task.

There will be m*(D-1)+1 tasks. Task T_{0} has deadline 1. There will be many other “dummy” tasks T_{ij }that are set up so that they have to start at time j and have to end at time j+1. This is enforced by having each T_{i,j} have to come before T_{i,j+1} in the partial order. The number of i tasks at each j changes:

- T
_{i,1}where i goes from 1 to m-k - T
_{i,2}, where i goes from 1 to m-e - T
_{i, 2+j}for each j from 1 to e, where i goes from 1 to v+j - T
_{i, e+2+j}, for each j from 1 to e, where i goes from 1 to a+v+j-1 - T
_{i, 2e+2+j}, for each j from 1 to e, where i goes from 1 to e+v

T_{0} is the root of all of these and comes before each T_{i,1}.

Then we add the “graph-dependent” tasks. Each vertex v_{i }has a task that has to come after T_{0} and has deadline e+3. Each edge e_{j} spawns two chains of j tasks (E’_{j}(1) through E’_{j}(j) and E”_{j}(1) through E”_{j}(j)) each, all of which have deadline 2e+3. We’ll also have two other groups of tasks for each edge: L’_{j}(1) through L’_{j}(a) and L”_{j}(1) through L”_{j}(a). The last E’ task for each j comes before all of the corresponding L’ tasks, and the last E” task for each j comes before all of the corresponding L” tasks. Each of these L tasks has a deadline D-j+1.

Edges in G also create precedence constraints. The edge e_{j}= (v_{q}, v_{r}), with q < r, creates the ordering v_{q}->E’_{j}(1) and v_{r}->E”_{j}(1).

If G has a Vertex Cover, then that means that each edge in E has at least one endpoint in the cover. The jobs for these vertices will be scheduled at time 1. Since the vertex jobs have to be run before the corresponding edge jobs, we can run the E’ or E” job that was set by the last precedence constraints above at time 2. Once that chain finishes, we can run the L jobs.

If we have a valid schedule that meets all deadlines, we must have scheduled T_{0} first, and each T_{i,j} at time j. This means we only have so many slots to schedule the E chains. Since vertex tasks have to happen before these chains, we need to schedule k vertex tasks at time 1. If the schedule is feasible, that must be a cover. (Because if it wasn’t, then there is an edge that does not have it’s E’ or E” chain start at time 2, which is too slow).

**Difficulty: **The easy way is a 2. The way from the paper is an 8, because it’s hard to see why the specific numbers of dummy tasks comes from, why you need 2 chains (E’ and E”. You need them because you don’t know which endpoint of the edge will be in the cover), why you need the L tasks, and so on.

**The problem: **Given a set T of tasks, each with a length of 1, a set of m processors, and set of r resources. Each resource i has a “bound” b_{i} of how many resources are available at any time and each task t has a requirement r_{i}(t) denoting how many of resource i it needs. We’re also given a deadline D.

Can we find an m-processor schedule for all tasks that:

1) Meets the deadline, and

2) For all time units, ensures that all of the processors running in that time unit do not exceed the resource bounds for any resource?

**Example:** Suppose I have 4 tasks and 1 resource:

- Task 1 uses 2 units of the resource
- Task 2 uses 3 units of the resource
- Task 3 uses 4 units of the resource
- Task 4 uses 5 units of the resource

If there are 5 units of the resource available, we can get this task done in 3 time steps (Task 4, then 1 and 2, then 3)

If there are 7 units available, we can get the task done in 2 time steps (1 and 4, then 2 and 3)

If there are 14 units available, we can do everything in one time step.

**Reduction: **The paper by Garey and Johnson proves several variants of the problem, The one I’ll do is in Lemma 3.4, which has 5 processors and 8 resources. They go on from there to show the variant with 3 processors and 1 resource is also NP-Complete, but we only care about the general problem.

The reduction is from 3DM, In this case, we’re assuming that W, X, and Y (the 3 sets in the 3DM problem) are all the same set T, and S is our set of triples from T. Define a value m_{k}(i) to be the number of triples in S that have i in their kth value. So m_{2}(4) is the number of triples with a 4 in position 2. Notice that for a legal matching to be possible, m_{k}(i) has to always be at least 1.

So we build an instance of the resource constrained scheduling problem with 5 processors and 8 types of resources. The tasks are:

- One “S” task for each triple in S.
- X
_{0}, Y_{o}and Z_{0}tasks for each element in q (the number of elements in T) - X
_{ij}tasks where i runs from 1 to q and j runs from 1 to m_{1}(i). - Similar Y
_{ij}(using m_{2}(i)) and Z_{ij}(using m_{3}(i)) tasks. - “F” tasks from 1 to q.
- |S|-q “G” tasks.

The deadline is |S|.

The resource requirements are set up so that during each time unit, we have 5 processors running:

- An S task
- An X or X
_{0} - A Y or Y
_{0} - A Z or Z
_{0} - An F or G

The X_{0}/Y_{0}/Z_{0} tasks all correspond to an element in T, and can only be run along with an F and the S task that is the triple of the X, Y, and Z elements in S. The F task makes sure that we have chosen a legal matching. The matching consists of the S triples chosen along with an F task. (The G tasks are there to fill the rest of the timesteps) .

**Difficulty: **7. More if you keep going and refine the problem down to 3 processors and 1 resource.

Here’s SS9, which uses the non-book problem from last week.

**The problem: **Precedence Constrained Scheduling. This is problem SS9 in the appendix.

**The description: **Given a set T of tasks arranged in a partial order, each of length 1, a set of m processors, and a positive deadline D. Can we schedule the tasks in T in a way that obeys the precedence constraints and finishes by deadline D?

**Example: **

Suppose we had the following simple precedence graph:

With 2 processors, the earliest feasible schedule will finish at time 3 (timestep 1 schedules 2 of a,c, and d, timestep 2 schedules the remaining one, and then we can finally schedule b after that). With 3 processors, we can finish at time 2 (schedule all of a,c, and d in timestep 1, then b right afterward).

**Reduction: **This is problem P2 in the paper by Ullman and the reduction uses the “Single Execution Time with Variable Number of Processors” problem from last week. The main difference between that problem and this one is that instead of having a varying number of slots at each time, now we have K processors for all time steps.

So we start with an instance of the variable processor problem and add a bunch of new jobs I_{ij }where i runs through each time step, and j runs from 0 to n – c_{i. }(n is the number of jobs, c_{i} is the number of processors available at time i). The precedence graph keeps all of the old precedences and also has I_{ij} come before I_{i+1,k} for all j and k. (So all I jobs at time i come before all I jobs at time i+1). We have n+1 processors and the same deadline. The idea is that the extra I jobs all have to run at their specific timestep, and they “fill” the empty processor slots between the c_{i} processors available in the original and the n+1 processor slots available in this version of the problem.

**Difficulty: **4, mainly because the variable # of processors problem we’re reducing from is hard to understand. But this is a classic “go from a variable number of things to a larger fixed number of things” technique that can be used for lots of problems.

]]>

**The problem: **Single Execution Time Scheduling. This problem is not in the appendix and is problem “P4” in the paper by Ullman that has this reduction and the problem next week.

**The description: **Given a set S of N jobs, arranged in a partial order, each taking 1 time unit, a deadline D, and a sequence of D “processor slots” arranged in a sequence from 0 to D-1, where the sum of all of the processor slots is exactly N, can we create a feasible schedule for all tasks that respects the partial order?

**Example: **Here’s a simple example of a partial order:

If D=3 and the processor slot sequence was {1,2,1}, then this can be solved: schedule a at time 0, b and c at time 1, and d at time 2. (So d is done by time 3).

If the processor slot sequence was {1.1.2}, then at time 1, we can only schedule 1 of b and c, so we won’t be able to schedule d at time 2.

**Reduction: **The Ullman paper goes from 3SAT. The original formula will have m variables and n clauses. We will create jobs:

- x
_{ij}and ~x_{ij}where i goes from 1 to m and j goes from 0 to m. - y
_{i}and ~y_{i}where i goes from 1 to m - D
_{ij}where i goes from 1 to n and j goes from 1 to 7.

The partial order on these jobs is:

- Each x
_{ij}comes before x_{i,j+1}and each ~x_{ij}comes before ~x_{i,j+1} - Each x
_{i,i-1}comes before y_{i}and each ~x_{i,i-1}comes before ~y_{i} - For each D
_{ij}represent the j index as a binary number (from 1 to 7). The three literals in clause i also have 7 combinations of ways to set their literals to make the clause true, which can also be represented as a binary number. Then for each binary combination, look at the positive or negative settings of the literals that make that combination true. Then take the*last*of the x (or ~x) jobs of the variables corresponding to those literals and make it come before the D_{i}job. So if we are considering job x_{k}, we’d make x_{km}come before the D_{i}job we’re looking at.

That last thing is just a way of saying “there are 7 ways of making the clause true, you need to execute the job of the literals that makes the clause true before you do the clause job.”

The deadline is n+3. The processor slots have:

- m slots at time 0
- 2m+1 slots at time 1
- 2m+2 slots at times 2-m
- n+m+1 slots at time m+1
- 6n slots at timem+2

The idea is that at time 0 we need to run one of either x_{i0} or ~x_{i0} for each i. (The other is run at time 1). These will correspond to whether variable i is set t true or false. We need to do that because we need to run the y jobs as soon as they become available (y_{0} or ~y_{0} – whichever is the same parity as the x variable we ran in time 1- needs to be run at time 1, and so on down). At time 1, we run either x_{i1} of ~x_{i1}, depending on what we do at time 0. So at time m+1, we have one y job left over (the last of the y’s in the sequence we started late), m x jobs left over (the x_{im} or ~x_{im} corresponding to the variable we started at time 1), and hopefully have enough x jobs finished to be able to run n D jobs (one for each clause). This is the way you’ll satisfy each clause. Then at time m+2, everything is done except for the other 6n D jobs.

**Difficulty: **7. I think Ullman does a very good job of explaining his method, which actually obscures a bit how complex this reduction is, and all of the moving parts and algebra going on.

]]>

**The problem: **Sequencing to Minimize Maximum Cumulative Cost. This is problem SS7 in the appendix.

**The description: **Given a set of tasks with a partial order defined on the tasks, and a (possibly negative) integer cost c(t) for each task t, and a positive value Z. Can we create a one-processor schedule that obeys the partial order constraints such that for each task t, the cost of each task scheduled before t is K or less?

**Example: **Suppose we have the following precedence graph of tasks (the number in the vertex is the cost of the task):

The schedule (d,e,a,c,b), has the last cost (task b) with a total cost of 12 (the sum of the costs of all tasks before it). Notice that if some task costs are negative, it’s possible that the last task that is done does not have the maximum total cost. For example, if task c’s cost was -4, then the schedule (d,e,a,c,b) has its maximum cost of 10 after task a is scheduled.

**Reduction: **I got this from a Ph.D. thesis by Abdel-Wahab, and in it, he reduces from Register Sufficiency. So we’re given an instance of register sufficiency: a directed graph G=(V,E), and a K. The precedence graph G’=(V’, E’) that we’ll build has 2 copies of every vertex in G. The edge set of G’ will have all of the edges in E, and additionally, if (i,j) is an edge in E, then (j’, i) is an edge in E’. (That’s an edge from the second copy of j, back to i.) Each vertex costs 1 if it was in V, and -1 if it is a second copy. Set K’ = K+1.

If the register sufficiency problem has a solution, then we build a sequence based on it. Thinking in terms of Sethi’s “stone game”, each time we place a new stone on a vertex, we add that vertex to the end of the sequence. If we pick up a stone from vertex x, add x’ (the second copy) to the end of the sequence. If we’re moving a stone from y to x, then that’s like removing it from y and adding it to x. You can prove by induction that the max cost of any task in this sequence is K’.

If the scheduling task has a solution, then we build a solution to the stone game. For each task in the schedule, if it is in V (the original set of vertices), then place a stone on that vertex. If it is not in V then it is a new vertex, so remove a stone from the copy in V. Since K’ = K+1, it’s possible that this sequence of steps uses K+1 stones, so we need to modify it to use K. So, find a node in the sequence whose cost is exactly K’. We know this node is in V (it was a “place a stone” move since it took us to K’ stones). It can’t be the last task since we have to end with one that costs -1 (since all of those vertices have no outdegree, and the ones that cost 1 all have an outdegree of at least 1). So look at the task after it. It *can’t* be a “place a stone” move, otherwise, we’ll use K’+1 stones. So we know that the next task to be scheduled has cost -1. Since that move will be to pick up a stone, just move the stone that’s about to be removed to our peak cost task instead of allocating a new stone, and we will solve the problem using 1 less stone at this point. If multiple places in the schedule cost K’ exactly, we can do this modification to all of them, creating a program sequence that uses just K stones at most.

**Difficulty: **6. The reduction is not hard, but the stone game takes some work to understand, and the turning a solution that costs K’ into a solution that costs K is a little tricky. I wonder if there’s an alternate reduction that has K’=K.

**The problem: **Register Sufficiency. This is problem PO1 (“Program Optimization”) in the appendix.

**The description: **Given a directed acyclic graph G=(V,A), and a positive integer K, can we find a “computation” for G that uses K or fewer registers? The idea behind a “computation” is that vertices represent values that need to be kept in registers, and edges show dependencies between these values. So can we represent the computation keeping K or fewer values in memory at all times?

**Example: **Here is the example graph from the paper by Sethi with the reduction (p.227):

So, for example, the “x” at the bottom is used twice: Once in t1 for the c*x calculation, and one at t3 for the (b+c*x)*x calculation. We’d like to keep the x in the same register for both calculations. The numbers of the vertices show the registers holding the values and lead to the following assembly-like computation:

- Load c into register 2
- Load x into register 3
- Multiply registers 2 and 3, putting the result in register 2.
- Load b into register 1.
- Add registers 1 and 2, putting the result in register 2.
- Multiply registers 2 and 3, putting the result in register 2.
- Load a into register 3.
- Add registers 2 and 3, putting the result into register 1.

**A related problem: **It’s worth mentioning that Sethi represents this computation problem as a “game” problem of placing stones on a graph. The stones are like registers. The possible moves are:

- Place a stone on a leaf (allocating that leaf’s value to a register)
- Pick up a stone from a node (reclaiming that register for another use)
- Place a stone on a non-leaf node if there is a stone on all of its children (generating a value and putting the result in a new register)
- Move a stone from a node to a parent node if all children of the parent have a stone (generating a value and putting the result in a register held by one of the children).

The problem then becomes: Can we get stones on all “root” nodes of G (nodes without parents) using K or fewer stones at all times?

**Reduction: **Sethi’s reduction is from 3SAT. The SAT instance has m clauses and n variables. K will be 5n+3m+1. The DAG that gets built has n+m “stages”. The first n stages “assign” truth values to variables, and the rest check that each clause is satisfied.

The variable states are a pretty crazy structure based on a vertex z_{i}, which can only be computed after all of its ancestors are computed, and when computed, there will be n-i+1 stones in hand. This is the number of stones needed to compute the node to x_{i} and ~x_{i}. So the last stone in that computation is placed on either x_{i} or ~x_{i}, “setting” its truth value (and not leaving any stones to compute the opposite value). The clause stages are set up so that we only have enough stones to compute the clause if the clause is satisfiable (using the computed stone sitting on the x_{i} or ~x_{i} vertex).

The actual reduction goes on for pages and pages and has a lot of details to worry about.

**Difficulty: **9. It’s a shame that the cool idea of the stones game still led to such a hard reduction.

**The problem: **Sequencing with Deadlines and Setup Times. This is problem SS6 in the appendix.

**The description: **We’re given a set of T tasks, each task with a length l(t) and deadline d(t). We also have a set of “compilers”, C, and each task has a specific compiler k(t). Each compiler has a setup time l(c). Can we find a one-processor schedule for T that meets the deadlines of all tasks with the additional constraint that if two tasks are scheduled with different compilers, we must pay the setup cost of the second task’s compiler before starting the second task?

**Example:** I’m not sure “compiler” is the best word for C. I think of it as more of a preprocessor (and in fact, the paper by Bruno and Downey that has the reduction calls it a “setup task”). Each task needs some preprocessing to be done before it can run, and if you run two tasks that need the same preprocessing in a row, you can do them both one after the other. Otherwise, you need the preprocessing to happen (immediately) before you start the task.

So, suppose I have 2 compilers:

Compiler | Setup time |

1 | 5 |

2 | 7 |

And 4 tasks:

Task | Length | Deadline | Compiler |

1 | 5 | 10 | 1 |

2 | 3 | 20 | 2 |

3 | 3 | 23 | 2 |

4 | 10 | 100 | 1 |

..Then if we schedule task 1 first, its setup + length gets it done at time 10. Then we do the setup for task 2, so it finishes at time 20. Task 3 uses the same “compiler”, so does not need to do setup time, so will finish at time 23. Task 4 uses a different compiler, so needs to re-run compiler 1’s setup time of 5, and will be done at time 38.

Notice that the most “efficient” schedule that minimizes the number of setups we have to do will not schedule all tasks by their deadlines.

**Reduction: **I’m pretty sure this problem is Theorem 1 in the paper by Bruno and Downey. They say they reduce from Knapsack, but since there aren’t any weights, they’re really using Sum of Subsets. The original set S has N elements, a target B, and the sum of all elements in the set is called t_{0 }(which will, of course, be > B). There will be N+2 “classes” of 3 tasks each that share the same setup task. The setup for all classes C_{i} takes time 1 and has 3 tasks: one that takes time 1, one that takes time s_{i} (the corresponding element in S), and a third task that takes time H*s_{i}, where H is the larger of t_{0} and N+2. The 2 “extra” classes C_{o} and C_{n+1} work similarly, but use t_{0} instead of s_{i}. The deadline for the first task in all classes is d_{1} = 2(N+2) + B. The deadline for the second task in all classes is d_{2} = 3N+5+3t_{0}+2Ht_{0}-HB. The deadline for the third task in all classes is d_{3}=3N+5+3t_{0}+3Ht_{0}.

They go on to show the following facts:

- In a feasible schedule, you can’t have a d
_{3}task finish before the d_{1}deadline (there is no time for it). - In a feasible schedule, you can’t finish the second task of C
_{0}or C_{n+1}before d_{1}. (Since the second task takes t_{0}time, feasibly scheduling everyone’s first tasks with their setup times does not leave enough time left). - In a feasible schedule, we can only setup 2N+3 setup tasks at most. (More leaves too much time for setup and processing and someone will miss their d
_{3}deadline). - In a feasible schedule, there is exactly one task that gets setup once (that is, it gets set up, and does its three tasks in sequence). This is because of the first bullet and the fact that we have a limit of setup tasks.
- In a feasible schedule, you never do the first task then the third task without doing the second task (You can rearrange other feasible schedules to have this property and remain feasible).

What results from these facts is that a feasible schedule has: a bunch of first tasks (preceded by the setup task), a bunch of first and second tasks in sequence (preceded by the setup task), and a single class that does its setup then all three tasks. This last sequence crosses over the d_{1} deadline. Then we need to set up and schedule the second and third tasks of all of the classes we only did the first task for before the d_{2} deadline. Then we will set up and schedule the last task of all classes that have a third task remaining.

It turns out that the classes we chose to do the first 2 tasks before the d_{1} deadline for have costs (and second task length) of exactly B.

**Difficulty: **7. This is a really good paper that does a really good job of proving each of the above facts along the way and showing how it all works (many other papers would resort to saying some of these proofs were “obvious”, which I don’t think they are). Having said that, I don’t know how anyone comes up with these classes- especially the deadlines- without weeks of trial and error, and that is too much for a student to manage on their own.

**The problem: **Knapsack. (More technically, “0/1 Knapsack”). This is problem MP9 in the appendix.

**The description: **Given a set U of items, each item u in U has a profit p(u), and a weight w(u). (G&J call these “value” and “size”), and positive integers B and K. Can we create a subset U’ of U that has total profit at least K, but total weight at most B?

**Example: **Knapsack is one of my all-time favorite problems, which probably says something about me. But I inflict it on my students at all levels of the curriculum – it’s a good motivation to introduce parallel arrays, and building simple classes at the low levels, the fractional version is a good example of a greedy algorithm, and the 0/1 version is a good example of where greedy fails. It also fascinates me how it’s an example of a problem where a problem that has infinite solutions (Fractional Knapsack, when you can take any proportion of an item) is easily solvable, but a problem that has fewer solutions to consider (0/1 Knapsack, where there are “just” 2 choices for each item) is intractable.

Anyway, here is my classic “Your Greedy Approach Won’t Work” example:

Item | Profit | Weight |

1 | 5 | 4 |

2 | 3 | 3 |

3 | 3 | 3 |

If B=6, the best option is to choose items 2 and 3. But greedily picking items by profit/weight ratio (which works for the Fractional Knapsack problem) will choose item 1, and lose profit.

**Reduction: **G&J reference Karp’s paper and this is a classic reduction you see in most books that do this problem. You go from Subset Sum. We’re given a set S, and an integer K. We create a set U with the same number of elements, make the profit and weight of each element the same, and equal to the size of the corresponding element in S. We set K’=B=K.

If the original set had a subset summing to K, then taking those elements will make us a profit of K’ and a weight of B.

If we have a Knapsack solution with profit K’ or more, then since the profits of all items are equal to their weights, the only for the total weight to not exceed B is for the profit to be exactly K’. So taking the corresponding items in S will get a sum equal to exactly K.

**Difficulty: **3. I use this as an easy homework or test question all the time.