**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.

**The problem: **Sequencing to Minimize Weighted Tardiness. This is problem SS5 in the appendix.

**The description: **Given a set T of tasks, each task t has a length l(t), a weight w(t) (which is really a penalty for finishing a task late) and a deadline d(t), and a positive integer K. Is there a one-processor schedule for all tasks in T where each task is penalized by w(t) for each time unit it goes beyond its deadline, such that the total penalty is K or less?

**Example: **Suppose I have 3 tasks:

Task | Length | Deadine | Weight |

1 | 3 | 3 | 100 |

2 | 1 | 4 | 3 |

3 | 2 | 4 | 1 |

If task 1 is going to not miss its deadline, it needs to start right away. Since the weight penalty for missing the deadline is pretty disastrous, we should do that.

At time 3, when task 1 is done, we have a choice: Schedule task 2, and have task 2 finish 2 time units late, or schedule task 3, have it finish just 1 time unit late and schedule task 2 afterward. With these weights, scheduling task 2 first gives a penalty of 2 (task 3 finishes 2 time units late, for a penalty of 1, and 1×2=2). The other plan would have a penalty of 7 (Task 3 finishes 1 unit late, and task 2 finishes 2 units late).

If the weight of task 2 was 1, and the weight of task 3 was 5, then scheduling task 2 first gives a penalty of 10, but scheduling task 3 first gives a penalty of 6.

**Reduction: **G&J reference a paper by Lawler, which itself references G&J. Weird. Anyway, they reduce from 3-Partition. Given a 3-Partition instance (a set of 3n elements, and a bound B. Each element is between B/4 and B/2, and the sum of all elements is m*B), we create 4n jobs. 3n of them will be “A” jobs, each one corresponding to an element in the 3-Partition instance. We will also have n “X” jobs.

Each “A” job A_{i} corresponds to an element in the 3-partition instance a_{i. }These jobs have weight and length B+a_{i} and deadline 0.

The X jobs all have processing time 16B^{2}+n(n+1)/2 + 1. Call this number L. The X jobs have weight (L+4B)*(4B)*n(n+1)/2 + 1. Call this number W. Each X job X_{i} has deadline iL+(i-1)*4B. Notice that these X jobs don’t depend on the values in the partition instance at all (they do depend on the size).

Our K value is W-1.

If the original set has a partition, then we can make a schedule of: X1 then the A jobs corresponding to a 3 element partition, then X2, then another set of 3 A jobs corresponding to another 3 element partition, and so on down. Since we know the 3 elements in a partition sum to exactly B, we know that the processing time of each set of 3 A jobs is exactly 4B. So each X job will finish at time i*L+(i-1)*4B, and not be tardy. The A jobs all have deadline 0 and will all be tardy and will pay penalties related to their completion times, but that will sum to less than K.

In the other direction, suppose that we have a legal schedule. Since all of the X jobs have weight > K, they all have to be scheduled before their deadlines. Then we can talk about the weights and completion times that the A jobs need to have to fit around these X jobs. It’s a lot of algebra to make it all work out, but the math isn’t *too* hard.

**Difficulty: **7. While the algebra is followable, the idea that you’d need to come up with these numbers in the first place is a hard one- I know they’re picked because they “work”, but I feel like there must be an easier way.

**The problem: **Sequencing to Minimize Weighted Completion Time. This is problem SS4 in the appendix.

**The description: **Given a set T of tasks, and a partial order on each task. Each task t T has a length l(t) and a weight w(t). We’re also given a positive integer K. Can we find a one-processor schedule for T that obeys the precedence rules (so if a b, then a is scheduled before b), and the sum of the completion time of each task * the weight of that task is K or less?

**Example: **Suppose we had 3 tasks:

Task | Length | Weight |

1 | 2 | 4 |

2 | 3 | 3 |

3 | 4 | 1 |

Suppose the only ordering constraint was that 1 had to be scheduled before 2. Then scheudling 1 first, then 2 seconds, then 3 third gives the following weights:

- 1 is finished at time 2, so gives weight 8
- 2 is finished at time 5, so gives weight 15
- 3 is finished at time 14, so gives weight 14.

..for total weight 37

If we also add a constraint that 3 had to be scheduled before 2, then there are only 2 feasible schedules (1,3,2) and (3,1,2). The (1,3,2) schedule has a total weight of 41, and the (3,1,2) schedule gives a total weight of 55.

**Reduction: **The paper by Lawler reduces from Optimal Linear Arrangement. So we start with an undirected graph. Each vertex becomes a task with processing time 1 and weight 0. Each edge gets an “in” job with processing time 0 and weight -1, and an “out” job with processing time 0 and weight 1. The “in” job has to come before the vertex it is going in to and the “out” job has to come afterward.

Recall OLA asks: Can we find an ordering of each vertex where f(v) is the position of vertex v, such that:

The construction of the tasks gives us the same weighting for the “in” and “out” tasks for each edge (the “out” task contributes f(v) to the total, and the “in” task subtracts f(u) from it). The only problem is that we should have positive lengths of jobs.

So let the in and out tasks take time 1, and vertices take time ||. Next, we can replace each vertex task with || tasks each of time 1, each linked in a precedence chain. Once that happens, we can increase the weights of all tasks by the same amount and not change the problem.

**Difficulty: **I think the first part is difficulty 4. Once you do the tricks with the numberings, it probably becomes a 6.

The next problem has a very similar title, but is a pretty different problem:

**The problem: **Sequencing to Minimize Tardy Task Weight. This is problem SS3 in the appendix.

**The description: **Given a set T of tasks, each with a length, a deadline, and a weight (think “penalty”), and an integer K, can we find a one-processor schedule for T such that the sum of the weights of the tasks that are not completed before their deadline is K or less?

**Example: **Here’s a pretty simple example to show what the hard decision is:

Task | Length | Deadline | Weight |

1 | 1 | 1 | 3 |

2 | 2 | 3 | 4 |

3 | 3 | 3 | 5 |

Obviously, we can only do either task 3, or both of task 1 and 2. The current weights favor doing tasks 1 and 2 (because we want to make the weights of missed tasks small), but changing the weight values can change the decision.

**Reduction: **This is done in Karp’s paper. We reduce from Partition. Let S be the set we are trying to Partition. We’ll have a task for each element in the S. The length of each task and its weight will be equal to the size of the element in S. K = half of the sum of all of the elements in S. The deadline for all tasks is also K.

Notice that the only way to get total weight K (or less) after the deadline is to get total length K (or more) done before the deadline. We can’t do *more* than K length before the deadline (since the deadline is K). So the only way to have a feasible schedule is to have a set of tasks whose lengths are exactly K, which is a partition.

**Difficulty**: 3. I think this is a little easier than last week’s since you don’t have to make up that boundary task in the middle. Still, going from one set of things (the elements in S) to 3 values (length, deadline, and weight) gives opportunities for confusion.

**The problem: **Sequencing with Release Times and Deadlines. This is problem SS1 in the appendix.

**Description: **Given a set of tasks, where each task t has a “release time” r(t), a “deadline” d(t), and a length(t), all positive integers, can we create a one-processor feasible schedule for these tasks that meets all deadlines?

The definition of “feasible” is pretty straightforward:

- No task can start before its release time.
- No two tasks can be running at the same time (and there is no preemption, so once a task starts running, it must complete)
- All tasks finish before (or equal to) their deadline.

**Example: **Here’s an example that will relate to the reduction:

Suppose I have 5 tasks: The first 4 tasks are similar: All of them start at time 1, all of them have a deadline at time 11, and the lengths are 1,2,3, and 4. Our fifth task starts at time 5, has a length of 1, and a deadline of 6. (So every feasible schedule must have this task own the processor between times 5 and 6)

A feasible schedule would be: {1,4,5,2,3}. 5 minutes of tasks before the 5th task, then 5 minutes of tasks afterward.

**Reduction: **Like I said above, I think this equivalent to the G&J “Sequencing Within Intervals” problem- at least I can’t see a difference. The reduction for that problem is on page 70 of the G&J book, and it’s pretty elegant.

We reduce from Partition. Let B = the sum of all elements in the Partition instance. Each element in the set will become a task with starting time 1, deadline B+1, and a length equal to the value of the set element. We have one extra task that is released at time B/2, has a length of 1, and a deadline of B/2 + 1 (this is like our 5th task in the example above).

The idea is that the only way to fit all of the tasks feasibly is to have some subset of the tasks start at time 1 and take (collectively) exactly B/2 time, then we have our 1 extra task, then we fill the time from B/2+1 to B+1 with the rest of the tasks (which also take collectively exactly B/2+1 time). This forms a partition of B.

**Difficulty: **3. I think this is a good reduction students can come up with on their own. I like the slickness of the extra element to force the partition. If the problem we’re talking about is actually *different* than the one done in G&J, we’d have to see just where the differences lie.

**The problem: **Safety of File Protection Systems. This is problem SR36 in the appendix.

**The description:** We’re given:

- A set R of “generic rights” (intuitively, these are things like “read access” or “write access”)
- A set O of “objects”
- A set S ⊆ O of “subjects”
- A matrix P defining the set of rights for each combination of set and objects. That is: for all s∈S and o∈O, P[s][o] defines the rights (a subset of R) that subject s has on object o.
- A set C of “commands” Each command takes a set of parameters X
_{1}through X_{m}, where each X_{i}is an object (and many are subjects as well). - The commands begin with a series of if statements: “if r
_{i}∈ P[X_{si}][X_{oi}]”, where “si” and “oi” are numbers from 1 to m. So the if statement is asking if a specific right exists for a specific pair of parameters to the command. The command will have several of these if statements connected by “and” statements. - If all of the clauses in the if statement is true then a list of several operations can happen. The two operations that can happen are “enter a right into P” or “delete a right from P”. I think you can also delete objects and subjects, but you can’t create them (in the version of the problem we’re talking about), so I don’t know if it matters

Can we find a sequence of commands in C where at some point in the sequence after executing a command, some set P[s][o] has a right r’ that it previously did not have?

**Example: **It’s pretty easy to come up with an example that is unsafe- just create a function that gives a right to someone. I think where it gets harder is when you deal with operations that have sets of commands. Here is an example from the paper by Harrison, Ruzzo, and Ullman that discusses this problem:

command IREAD(s1, s2, o){ if "read" ∈(s2, o) and "iread" ∈ (s1, s2) then enter "read" into (s1, o) delete "read" from (s1, o) }

The command “iread” is meant to stand for “indirect read”. So what this is saying is that s2 can read 0, and s1 has the rights to read what s2 reads. So s1 gets (temporary) permission to read 0. Presumably, some time passes in between the granting of the read permission to s1 and the removal of that right, during which the data in o gets read by s1. Notice that this is a safe procedure because, by the end, no new rights have been granted.

**Reduction: **The paper referenced above shows that a system in which we also have operations that can create objects and subjects is undecidable. They do this by showing how this system can simulate an arbitrary Turing Machine. The reduction turns each transition into a command in the system: The current state, the current cell the head is over, and the contents of that cell are implemented as rights, and then state transitions can be seen as commands: The “if” parts of the command check that the machine is in the correct configuration, and the “then” parts change rights in a way that simulates the moving of the head, the output to the tape, and the transition to a new state. Importantly for our purposes, the only time these commands use the “create” commands (that are in the undecidable version of the problem, but not in ours) is when the Turing Machine moves the head into a previously unexplored area of the tape.

They then say that in our problem, which doesn’t have “create” commands, a simulation “similar to that used” in the undecidability reduction can also be used to convert an LBA into a set of commands, since an LBA won’t be moving its head arbitrarily into new space. I think the LBA still is allowed to use *some* new space though, but I guess that since it is bounded by a polynomial in the input size, we can simulate that by having each cell get it’s own access rights, and that keeps us with a polynomial-sized reduction.

**Difficulty**: 8. This is a hard problem to understand, and the reduction is done in a non-standard way (and depends on the LBA acceptance reduction which is *also* done in a non-standard way), so it may throw some people for a loop. It is a cool example of showing non-standard ways to reduce things though if that’s what you’re looking for.

**The problem**: Linear Bounded Automaton Acceptance. This is problem AL3 in the appendix.

**The description: **Given a Linear Bounded Automaton L, and a string x, does L accept x?

**Example**: A well-known context-sensitive language is L= a^{n}b^{n}c^{n}. If we had a definition for an LBA that accepts a string L, and a string such as aabbccc, the LBA should say “no” on this instance. But if our string was aabbcc, the LBA would say “yes”.

**Reduction: **G&J say this is a “Generic reduction”, and I can see why. Let me try to add some explanation and put it into something more like what we’re used to:

We start with a known NP-Complete (not NP-Hard) problem. Let’s say 3SAT. 3SAT is in NP, which means we have a non-deterministic Turing Machine that takes an instance x and solves 3SAT in polynomial time, let’s say p(|x|). Let’s assume that the alphabet of this Turing Machine is {0,1}.

Given this instance x of 3SAT, we build the following context-sensitive language (and it’s implied that we can go from there to an LBA in polynomial time). {#^{p(log(|x|)} x #^{p(log (|x|)}} over the alphabet {0,1,#}. So we have a number of # symbols before and after x equal to the polynomial on the log of the input.

To be honest, I’m not sure why you need the log here. I think you can get away with just having p(|x|) symbols on each side, and the total length of the LBA acceptance instane is still polynomial in |x|. The idea is that since we know that the TM completes in time p(|x|), it can only ever move its head p(|x|) tape symbols to the left or right before it runs out of time. So we can use that as the “bound” on the LBA.

So, if our LBA uses the exact same states and transitions as the non-deterministic Turing Machine that solved 3SAT, we now have our LBA accept x exactly when x was satisfiable.

The reason this is a “generic” reduction is that nothing we did had anything to do with 3SAT specifically. We could do this process for any problem in NP. It’s more useful if we start with an NP-Complete problem, but we could do this for things in P as well since they are also in NP.

**Difficulty: **5, mainly because this is a weird way of doing things, and Linear Bounded Automata are things that often get short shrift in Theory of Computation classes.

**The problem: **Consistency of Database Frequency Tables. This is problem SR35 in the appendix.

**The description:** I find G&J’s definition confusing, so this definition borrows a lot from the paper by Reiss that has the reduction.

We have a set of “characteristics” (attributes) A. Each attribute a in A has a domain D_{a}. A database is a set of “Objects” (tuples) O_{1}..O_{n} where each object defines a value for each characteristic. (The result is a two-dimensional table where rows are objects and columns are attributes). We can define a *frequency table* for each pair of attributes a and b in A. The table has |D_{a}| rows and |D_{b}| columns, and each entry (x,y) is “supposed” to represent the number of tuples in O that have x for its A attribute and y for its B attribute.

What the problem is asking is: Given a set of tables and a database table V, can we find a way to map the attributes in A to the tables such that the tables *actually* represent the frequencies in the database?

**Example:
**Since you need a frequency table for each pair of attributes, here is an example with 3 attributes, each taking 2 posible values. Attribute a’s domain is {0,1}, b’s is {a,b}, and c’s is {>, <}. Our set of objects is:

- (0,a,>)
- (0,b,>)
- (1,b,>)
- (0,b,<)

If we are handed a set of 3 frequency tables:

C_{1} vs C_{2}:

1 | 0 |

2 | 1 |

C_{1} vs C_{3}:

0 | 1 |

1 | 2 |

C_{2} vs C_{3}:

1 | 2 |

0 | 1 |

These frequency tables are accurate if C_{1} is attribute a, C_{2} is attribute b, and C_{3} is attribute c.

**The reduction**: From 3SAT. (Actually, this might be from regular CNF-SAT, but there is no reason not to restrict the input to 3 literals per claue). We take a SAT instance with p clauses and q variables and bake a database with 2 objects per variable (one for positive, one for negative) and p*q extra objects. We have one attribute per clause (“CL_{i}“), one attribute per variable (“VR_{i}“) and “VALUE” (which holds the truth value of the object in the final setting). The frequency tables are set up to ensure that each variable has one value setting(true or false) and each clause is made true. The way to make the database consistent with the table is to find a way to map the variables in the database to the tables in a way to make the formula satisfiable.

**Difficulty: **5, because the reduction isn’t that hard to follow, or come up with, once you get how the frequency tables work. But unlike most 5’s, I don’t think I’d assign it as a homework problem, because of how much extra work it would take to explain the frequency tables in the first place.

**The problem: **Safety of Database Transaction Systems. This is problem SR34 in the appendix.

**The description: **Given a set of variables V and transactions T as defined in the Serializability of Database Histories problem, is *every* history H for T equivalent to some serial history?

**Example: **This is an extension of last week’s problem. So last week’s example which produced a history that is not serializable means that that set of transactions is not safe.

The easiest way to produce transactions that are safe is to make them access different variables: Suppose I have 2 transactions (R_{1}, W_{1}) and (R_{2}, W_{2}). If the first transaction reads and writes a variable x and the second transaction reads and writes a variable y, then any ordering of those transactions will be serializable.

**Reduction: **It’s in the same paper by Papadimitriou, Bernstein, and Rothnie as the Serializability of Database History problem was. It’s interesting that they couldn’t show that the problem was in NP.

They reduce from Hitting Set. They show in the paper how to take a transaction system and build a graph where there is one vertex for each read and write operation in each transaction, and edges between the two operations in a transaction. There are also edges between operations R_{i} and W_{j} or W_{i} and W_{j} if those operations share a variable. These edges show places where changing the order changes the meaning of a transaction history. They show that a transaction system is safe if and only if the graph has no cycles containing a (R_{j}, W_{j}) edge. (Note that means that cycles can exist as long as they contain only W-W edges)

So, given an instance of hitting set, (a set S and a collection C of subsets of S), they build a transaction graph: One read vertex for each set in S, plus one write vertex at the end. Between read vertices R_{i} and R_{i+1} we add |C_{i}| edges (or, so we still have a simple graph, |C_{i}| paths containing vertices that don’t appear anyplace else). At the end of this chain of paths is a single W vertex, with an edge back to R_{1}. The only unsafe cycle now starts at R_{1}, goes through one of the paths connecting each R vertex, goes to the final W vertex, and then back to R_{1}.

So far, so good. But then they lose me when they say “We can embed the hitting set problem –among others– in safety by forcing (by the use of sets of singletons) each such choice to correspond to a hitting set”. I *think* what they’re saying is that they will create a set of variables corresponding to sets in C such that an unsafe cycle exists if and only if S has a hitting set. But I’m not sure how they get there- especially in polynomial time. I’m sure there’s a way, but it reads like “we set it such that it all works”, which isn’t convincing to me.

**Difficulty: **9, because I don’t see how they do that last step. I’m sure a good explanation exists that would make this less difficult. I’ll also say that the reduction also says the transaction system is unsafe “if and only if there exists an unsafe path–and therefore a hitting set”. Which sounds like a Co-NP proof to me. I’m probably missing something.

**The problem: **Serializability of Database Histories. This is problem SR33 in the appendix.

**The description: **We have a set V of variables in our database, and a set T of transactions, where each transaction i has a read operation (R_{i}) that reads some subset of V, and a write operation W_{i} that writes some (possibly different) subset of V. We’re also given a “history” H of T, which permutes the order of all reads and writes maintaining the property that for all i, R_{i} comes before W_{i} in the history. Think of this as a set of parallel transactions that reach a central database. H is the order the database processes these operations.

Can we find a serial history H’ of T, with the following properties:

- Each R
_{i}occurs immediately before its corresponding W_{i}. - A “live transaction” is a transaction (R
_{i}, W_{i}) where either W_{i }is the last time a variable is written before the R_{j}of some other live transaction or the last time the variable is written at all. The set of live transactions in H and H’ needs to be the same. - For each pair of live transactions (R
_{i}, W_{i}) and (R_{j}, W_{j}), for any variable v in W_{i}∩R_{j}, W_{i}is the last write set to contain v before R_{j}in H if and only if W_{i}is the last write set to contain v before R_{j}in H’. The paper says that this means transaction j “reads from” (or “reads v from”) transaction i.

**Example: **The paper by Papadimitriou, Bernstein, and Rothnie that has the reduction has a good simple example of a non-serializable history:

H= <R_{1}, R_{2}, W_{2}, W_{1}>, where R_{1} and W_{2} access a variable x, and R_{2} and W_{1} access a variable y. Both transactions are live since they both write their variables for the last time. Notice that neither transaction reads any variable. But the two possible candidates for H’ are: <R_{1}, W_{1}, R_{2}, W_{2}> (where R_{2} reads the y written by W_{1}) and <R_{2}, W_{2}, R_{1}, W_{1}> (where R_{1} reads the x written by W_{2}), so neither H’ candidate has the same set of transactions reading variables from each other.

**Reduction: **Is from Non-Circular Satisfiability. Given a formula, they generate a “polygraph” of a database history. A polygraph (N,A,B) is a directed graph (N,A) along with a set B of “bipaths” (paths that are 2 edges long). If a bipath{(v,u), (u,w)} is in B, then the edge (w,v) is in A. So, if a bipath exists in B from v to w, then an edge in A exists from w back to v. This means that we can view a polygraph (N,A,B) as a family of directed graphs. Each directed graph in the family has the same vertices and an edge set A’ that is a superset of A and contains at least one edge in each bipath in B. They define an *acyclic* polygraph as a polygraph (represented as a family of directed graphs) where at least one directed graph in the family is acyclic.

In the paper, they relate database histories to polygraphs by letting the vertex set N bet a set of live transactions. We build edges in A (u,v) from transactions that write a variable (vertex u) to transactions that read the same variable (vertex v). If some other vertex w also has that variable in their read set then the bipath {(v,w), (w,u)} exists in B. So edges (u,v) in A mean that u “happens before” v since u writes a variable that v reads. A bipath {(v,w), (w,u)} means that w also reads the same variable, so must happen before u or after v. They show that a history is serializable if and only if the polygraph for the history is acyclic.

So, given a formula, they build a polygraph that is acyclic if and only if the formula is satisfiable. The polygraph will have 3 vertices (a_{j}, b_{j}, and c_{j}) for each variable x_{j} in the formula. Each a vertex connects by an edge in A to its corresponding B vertex. We also have a bipath in B from the b vertex through the corresponding c vertex back to the a vertex.

Each literal C_{ik} (literal #k of clause i) generates two vertices y_{ik} and z_{ik}. We add edges in A from each y_{ik} to z_{i(k+1)mod 3} (in other words, the y vertex of each clause connects to the “next” z vertex, wrapping around if necessary). If literal C_{ik} is a positive occurrence of variable X_{j}, we add edges (c_{j}, y_{ik}) and (b_{j}, z_{ik}) to A, and the bipath {(z_{ik}, y_{ik}), (y_{ik}, b_{j})} to B. If the literal is negative, we instead add (z_{ik}, c_{j}) to A and {(a_{j}, z_{ik}), (z_{ik}, y_{ik})} to B.

If the polygraph is acyclic (and thus the history it represents is serializable), then there is some acyclic digraph in the family of directed graphs related to the polygraph. So the bipath {(b_{j}, c_{j}), (c_{j}, a_{j})} will have either the first edge from b-c (which we will represent as “false”) or will have the second edge from c-a (which we will represent as “true”). (The directed graph can’t have both because its edges are a superset of A, which means it has the edge (a_{j}, b_{j}) and taking both halves of he bipath will cause a cycle).

If our acyclic directed graph has the “false” (b-c) version of the edge for a literal, then it also has to have the z-y edge of the bipath associated with the literal (otherwise there is a cycle). If *all* of the literals in a clause were set to false, this would cause a cycle between these bipath edges and the y-z edges we added in A for each clause. So at least one literal per clause must be true, which gives us a way to satisfy the formula.

If the formula is satisfiable, then build the acyclic digraph that starts with all of A, and takes the bipath edges corresponding to the truth value of each variable, as defined above. This implies ways you need to take the edges from the bipaths for the literals, to avoid cycles. The only way now for the graph to be acyclic is for there to be a cycle of x’s and y’s in the edges and bipath edges. But that would imply that we’ve set all of the literals in a clause to false. Since we know that the clause can be made true (since the original formula is satisfiable), we know that a way exists to make the directed graph acyclic.

**Difficulty: **7. It takes a lot of baggage to get to the actual reduction here, but once you do, I think it’s pretty easy and cool to see how the cycles arise from the definitions of the graphs and from the formula.