CSci 280: Algorithms and problem-solving paradigms
Home Syllabus Classwork

Final Quiz: Questions

Fin.1.

[10 pts] Following is a poor implementation of merge sort in Java. In terms of n, the number of elements in the parameter ArrayList, how fast is it on a single processor? Provide the simplest and tightest big-O bound that you can, and justify your answer.

public static void mergeSort(ArrayList<Integer> nums) {
    if(nums.size() <= 1) return;

    ArrayList<Integer> a = new ArrayList<Integer>();
    ArrayList<Integer> b = new ArrayList<Integer>();
    for(int i = 0; i < nums.size(); i++) {
        if(i % 2 == 0) a.add(nums.get(i));
        else           b.add(nums.get(i));
    }
    
    mergeSort(a);
    mergeSort(b);
    
    nums.clear();
    while(!a.isEmpty() && !b.isEmpty()) {
        if(a.get(0).intValue() < b.get(0).intValue()) nums.add(a.remove(0));
        else                                          nums.add(b.remove(0));
    }
    nums.addAll(a);
    nums.addAll(b);
}
Fin.2.

[10 pts] Suppose we perform a depth-first search on the below dag, recursing on adjacent nodes in alphabetical order, using the algorithm described in class. What is the pre and post number that is associated with each vertex?

    
 pre  post 
b
c
d
e
f
g
h
i
Fin.3.

[10 pts] Software for typesetting text must automatically decide where to place the line breaks between words in a paragraph. One approach for this, used in high-quality typesetting programs such as TEX, is to define a cost function computing with the badness of each line, defined by a function c(ij), which might be defined as ∞ if the line including words i through j simply is too long to fit into the available space; but if the words fit, it yields a number such as

c(ij) = (pageWidth − (∑k = ij wordWidthk) − spaceWidth ⋅ (j − i + 1) .

(You don't have to worry about this formula for this problem.)

Assuming we have the badness function already defined, we can write a recurrence for computing the smallest overall badness for each possible way to break lines in a paragraph of words.

f(j) = { c(1, j) if c(1, j) ≠ ∞
min1 ≤ k < j (f(k) + c(k + 1, j)) if c(1, j) = ∞

Using dynamic programming, convert this recurrence into a Java method that takes O(n²) time, assuming that a method c has already been defined and takes O(1) time for each invocation. The c method returns Integer.MAX_VALUE to signify an infinite return value.

public static int f(int n) {
Fin.4.

[14 pts] In the DOMINATING SET problem, we're given a directed graph G and an integer k, and we wish to find a set of k vertices in G so that every vertex is either in the selected set or has an edge coming from the selected set into it. For example, in the dag of Problem 2, the set of vertices {adeh} is a dominating set with four vertices, and there are many other four-vertex dominating sets; however, {beg}. is the only three-vertex dominating set.

Using the fact that SET COVER is already known to be NP-hard, prove that DOMINATING SET is NP-hard. (If you cannot come up with a reduction that works perfectly, you can still receive partial credit (6 pts) for giving an outline of how one would prove this.)

Fin.5.

[8 pts] We manage a cargo plane with three compartments: front, center, and rear. The front compartment can hold at most 10 tons, the center compartment 16 tons, and the rear compartment 8 tons.

In order to balance the plane, each compartment must be filled to the same proportion by weight. Thus, if the front compartment holds 5 tons, then the center compartment must hold 8 tons, and the rear compartment must hold 4 tons.

We have four customers who have requested shipment. Each is perfectly happy if we choose to ship only a fraction of the request. We may split any shipment among the three compartments as we like, and we may mix multiple shipments' cargo into the same compartment.

weightprofit
(tons)($ per ton)
A18310
B15380
C23350
D12285

Write a linear program that determines how much of each customer's request to satisfy and how to apportion each shipment among the compartments. Be sure to explain what your variables mean; you need not justify your answer further.

Fin.6.

[8 pts] Execute the Ford-Fulkerson algorithm for computing the maximum flow of the directed graph. With each step of the algorithm you should always choose the available path with fewest edges, breaking ties by choosing the maximum-capacity path. Identify each path selected, and draw a graph showing the final flow along each edge.

edge capacitiespaths taken (you need not use all blanks)edge flows (final result)
1.
2.
3.
4.
5.
Fin.7.

[6 pts] When studying edge coloring, we saw that χ′(G) ≥ Δ(G). Explain why this is true.

Fin.8.

[6 pts] Describe Karger's O(n4) algorithm for finding the minimum cut in an unweighted graph.

Fin.9.

[10 pts] Suppose we're using an n-processor shared-memory system allowing concurrent reads. We have an n-integer array p representing a tree, with one node for each array element, where p[i] is the index of the node that is node i's parent (or −1 if the node is the root). We have another n-integer array d, initially all 0's, which we want to assign so that d[i] is the depth of node i. The following illustration gives an example input p, the tree that it describes, and the desired output d.

    
pp = p[pid];
d[pid] = pp >= 0 ? 1 : 0;
while(pp >= 0) {
    dd = d[pid] + d[pp];
    d[pid] = dd;
    pp = p[pp];
    p[pid] = pp;
}

To do this, each processor executes the algorithm given at right. (All processors execute this code in lockstep.) Remembering that we have a processor for each node of the tree, how much time does this algorithm take? Give the tightest and simplest big-O bound that you can, and justify your answer. Express your answer in terms of n, the number of nodes in the tree, and/or d, the tree's depth.

Fin.10.

[10 pts] Suppose we're given an n-element array of (θ, d) pairs, representing a sequence of commands to a turtle to turn counterclockwise θ degrees and then go forward d units. Below is an example array and the corresponding movement it represents, where the turtle's initial orientation is to the east — though the first command immediately turns the turtle to face northeast.

array of commandsmovement diagram
<(45, 40), (30, 50), (105, 40), (90, 20)>

Suppose we have a p-processor system, and we want to compute the turtle's final location in O(n/p + log p) time. Define an algorithm, using the parallel scan and/or parallel prefix scan algorithms we saw in class. Argue briefly that your algorithm works correctly.

Note: You may not find this useful, but: If the first two commands are (θ0d0) and (θ1d1), then after executing the command the distance from the turtle's starting location to its current location is

sqrt(d0² + d1² + 2 d0 d1 cos θ1).
Fin.11.

[8 pts] Suppose we're working on a problem where n is the number of bits in the problem input and p is the number of processors. We develop an algorithm whose time requirement is

O(n²/p + log² p) .

Can we conclude that this problem in the NC complexity class? Explain why or why not.

Final Quiz: Solutions

Fin.1.

The overall time taken by this poor merge sort implementation is O(n²).

While the first loop still takes O(n) time, the second loop — which performs the merge — now takes O(n²) time. This is because it repeeatedly removes from the front of the ArrayList, and each removal necessitates shifting all existing elements forward; thus, each iteration of the loop takes O(n) time, and there could be as many as n iterations of this loop. As a result, we can build the following recurrence describing the time for this method.

T(n) = { C if n ≤ 1
2 T(n/2) + D n² if n > 1

This recurrence matches the form for the Master Theorem, with a = 2, b = 2, and d = 2. Since logb a (which is log2 2 = 1) is less than d, the Master Theorem says that the solution to this recurrence is O(n²).

Fin.2.
 pre  post 
23
b110
c45
d69
e017
f78
g1114
h1516
i1213
Fin.3.
    int[] table = new int[n + 1];
    for(int j = 1; j <= n; j++) {
        if(c(1, j) == Integer.MAX_VALUE) {
            int min = Integer.MAX_VALUE;
            for(int k = 1; k < j; k++) {
                int cv = c(k + 1, j);
                if(cv != Integer.MAX_VALUE) {
                    if(table[k] + cv < min) min = table[k] + cv;
                }
            }
            table[j] = min;
        } else {
            table[j] = c(1, j);
        }
    }
    return table[n];
}
Fin.4.

Suppose we're given a SET COVER instance consisting of a collection {S0S1, …, Sm − 1} of subsets of some universe V, along with some target number t of subsets to select in order to cover U. We will show how to convert this into a DOMINATING SET problem whose solution solves the original SET COVER problem.

Our corresponding DOMINATING SET graph will have a vertex for each element of the universe U as well as a vertex for each subset Si and another grandparent vertex. We will have a directed edge from this grandparent vertex to each vertex corresponding to a subset Si. For each subset Si, we will also have a directed edge from the subset's vertex to the vertex corresponding to each element of the subset. Our target k for this DOMINATING SET problem will be t + 1 — i.e., one more than for the original SET COVER problem.

We need to show that there is a solution to the SET COVER problem if and only if there is a solution to the constructed DOMINATING SET problem. We first do the only if: Suppose there were a SET COVER consisting of t subsets. We could then select each of the vertices in the DOMINATING SET graph corresponding to one of the selected subsets; this would cover all vertices corresponding to elements of U, since the subsets themselves cover all of U. We will also select the grandparent vertex, which covers itself and all the remaining vertices corresponding to subsets. This covers all vertices in the graph, and so there is a solution to the DOMINATING SET problem using t + 1 vertices.

Now we go in the other direction: Suppose there is a DOMINATING SET solution — does that imply a solution to the original SET COVER problem? First, observe that the DOMINATING SET solution need not include any vertices corresponding to elements from U: If such a vertex is selected, we can unselect it, and instead select a vertex corresponding to a subset containing that element. So we'll assume that no vertices corresponding to U elements are selected. Our solution to SET COVER is then simply to select all those subsets corresponding to vertices selected in the DOMINATING SET solution. The subsets so selected will end up covering all of U, because their corresponding vertices cover all of the vertices corresponding to elements of U. The number of such subsets cannot exceed t, since at most t + 1 vertices are selected, of which one will have to be the grandparent vertex (since there is no other way to cover that vertex), and as we've seen, we can ensure that none of the selected vertices correspond to elements of U. Thus the other t must correspond to subsets.

Fin.5.

We define variables fA, fB, fC, and fD representing the amount (measured in tons) of each customer's shipment placed in the front compartment; cA, cB, cC, and cD representing the amounts for the center compartment; and rA, rB, rC, and rD representing the amounts for the rear compartment.

maximize 310 (fA + cA + rA) + 380 (fB + cB + rB) + 350 (fC + cC + rC) + 285 (fD + cD + rD)
where fA + fB + fC + fD 10
cA + cB + cC + cD 16
rA + rB + rC + rD 8
(1/10) fA + (1/10) fB + (1/10) fC + (1/10) fD = (1/16) cA + (1/16) cB + (1/16) cC + (1/16) cD
(1/10) fA + (1/10) fB + (1/10) fC + (1/10) fD = (1/8) rA + (1/8) rB + (1/8) rC + (1/8) rD
fA + cA + rA 18
fB + cB + rB 15
fC + cC + rC 23
fD + cD + rD 12
fx 0  for x in {A, B, C, D}
cx 0  for x in {A, B, C, D}
rx 0  for x in {A, B, C, D}
Fin.6.
paths takenedge flows
1. s-a-b-t
2. s-c-d-e-t
3. s-c-d-b-a-e-t
Fin.7.

This inequality says that the number of colors required for a valid edge coloring must be at least the graph's maximum degree. Consider the vertex whose degree is maximum: Each edge incident on that vertex must be given a different color, since they all have that vertex in common. Thus we need at least as many colors as there are edges incident to the vertex — which is to say that we need at least as many colors as the degree of the vertex, which we selected so that its degree is Δ(G).

Fin.8.

Karger's algorithm is to randomly select an edge in the graph and combine (contract) its two endpoints into a single vertex, merging the edges of the two endpoints together (keeping any duplicate edges so created). We repeat this process until our graph has just two vertices left; the remaining edges are the edges in our candidate cut. This algorithm takes O(n²) time, since each contraction takes O(n) time and there will be n − 2 contractions.

However, this algorithm is not likely to find the minimum cut — the probability that it finds the minimum cut is about 1/n². Thus, Karger's algorithm repeats the process of the previous paragraph O(n²) times, so that it is likely to happen upon the true minimum cut. It outputs the smallest cut that it finds among all these attempts.

Fin.9.

O(log d). The loop maintains three invariants: First, pp matches p[pid]. Second, d[i] is the distance from i to p[i] in the original tree. And finally, if p[i] is not −1, then d[i] is 2k, where k is the number of iterations of the loop thus far.

It is easy to see that this holds entering the loop: pp was initialized to p[pid], d[i] was initialized to 1 for everything but the root, and to 0 for the root; that is indeed the distance from node i to the root. And finally, for p[i] is not −1 for all non-root nodes, and for all of those i, d[i] is 1, which matches 20 — and there have been 0 iterations at the beginning.

If the invariant holds at the loop's beginning, it will still hold at the end. The first part about pp matching p[pid] obviously still holds, because the loop's final assignment assures this. The second part holds because dd is computed as the distance from node pid to pp's parent — and by the end of the loop, p[pid] has changed to be the parent of what pp referenced at the iteration's beginning. And finally, if it happens that pp's parent is not −1 (and thus p[pid] is not −1 at the iteration's end), then d[pp] must be 2k at the loop's beginning, as must be d[pid], so the value of d[pid] must end up being 2 ⋅ 2k = 2k + 1. Since k + 1 is the number of iterations completed at the iteration's end, we still have that any entry for which p is not −1, the corresponding entry in d is 2# iters.

The consequence of this invariant is that each entry in d continues to double until the final iteration. Since no entry in d can exceed d, it follows that the number of iterations must not exceed ⌈log2 d⌉.

Fin.10.

I know of two very different approaches to this problem.

  1. In the first, we define a new operator ⊗:

    (θ, x) ⊗ (φ, y) = (θ + φ, sqrt(x² + y² + 2 x y cos φ))

    Essentially, this operator is saying that performing the two commands (θ, x) and (φ, y) is equivalent to turning an angle of θ + φ just once and going a distance of sqrt(x² + y² + 2 x y cos φ)) in that direction.

    We can then perform a parallel scan of the array using this operator, ending up with a final result of (θtotal, dtotal). That is the final location in polar coordinates; if we want the location in Cartesian coordinates, we can compute those as (dtotal cos θtotal, dtotal sin θtotal)

  2. The second approach is converts the computation into Cartesian coordinates earlier. We first apply a parallel prefix sum to the first entry of all the pairs, thus converting the turn angles to compass directions. In the example of the problem, our pairs would become: <(45, 40), (75, 50), (180, 40), (270, 20)>.

    Then we convert each pair (φ, d) into (d cos φ, d sin φ), thus getting the change in x-coordinate and change in y-coordinate of each movement. And finally we perform two parallel sums, one for each of the coordinates. We would end up with the total change in x-coordinate and total change in y-coordinate.

Fin.11.

Yes, it is in NC. After all, if we were given n4 processors   that is a number that is polynomial in n  , then our algorithm takes just O(log² n4) = O((4 log n)²) = O(16 log² n) = O(log² n) time, which is polylogarithmic.