Past exam of the computer science course of the University of Oxford 2015 Compilers 3 e Solution by
Codex 0 2026-10-31
The source rewrite already saves the old
a[i] in register-allocated t. Denote the original integer index by , its original element by , and the original element there by . The second fragment first sets t=j, then writes to a[i0], and finally writes to a[j]. Thus it performs the same intended swap without needing to recompute the second memory address from a possibly modified array element.For completeness, the first fragment is also correct when the two locations might alias. If , its middle store changes only
a[j], so the final a[i] still denotes the first location. If , then , and the supposed swap is a no-op in both fragments. There is no need to assume distinct locations.For the second fragment, common subexpression elimination of maximal addresses gives, using
z for the second address:MOVE(R(u), ADD(C(A),SHL(M(ADD(R(fp),C(I))),2)))
MOVE(R(t), M(R(u)))
MOVE(R(z), ADD(C(A),SHL(R(t),2)))
MOVE(M(R(u)), M(R(z)))
MOVE(M(R(z)), R(t))The nested memory-to-memory assignment requires an additional scratch processor register
rv on a reduced instruction set computer. A direct translation is therefore:MOV rb, #A
LD ri, [fp + I]
LSL ru, ri, #2
ADD ru, rb, ru
LD rt, [ru]
LSL rz, rt, #2
ADD rz, rb, rz
LD rv, [rz]
ST rv, [ru]
ST rt, [rz]The same compiler need not produce any better code: this has the same ten-instruction count as part (c). It still performs two element loads, two element stores, two shifts and two separately materialized address additions, plus the base and local-index setup. The processor register roles and store order differ, but the work is the same. The corresponding folding of register-offset addressing removes both
ADD instructions here too, yielding the same eight-instruction count as part (d).The key alias analysis advantage available in both fragments is that
i remains unchanged by the array stores. In the second fragment, t is also unchanged after its first assignment because it lives in a processor register. Its name alone does not produce a further saving beyond what the first fragment's common subexpression elimination already achieves. Exact instruction selection is not uniquely prescribed by the paper; these explicit translations demonstrate why the claimed source-level efficiency advantage does not follow: Past exam of the computer science course of the University of Oxford 2015 Compilers 3 d Solution by
Codex 0 2026-10-31
The target's register-offset addressing already performs the addition of the array base and a scaled index as part of each load or store. Materializing a maximal common address expression into a processor register introduces an explicit
ADD that this addressing mode could have supplied. Here the extra address temporary variables save no arithmetic that the memory instructions need to perform separately.Instead keep the scaled indices, share the loaded old value of
a[i], and fold the base addition into each memory operand:MOV rb, #A
LD ri, [fp + I]
LSL rj, ri, #2
LD rv, [rb + rj]
LSL rk, rv, #2
LD rt, [rb + rk]
ST rv, [rb + rk]
ST rt, [rb + rj]rj remains the original index's byte offset, rv is its old element, and rk remains the second element's byte offset. Both stores use those saved offsets. Folding address additions into the memory instructions removes two instructions, reducing ten to eight, or eleven to nine if the local load requires the alternative sequence described in part (c). ThereforeThis illustrates why common subexpression elimination must interact with instruction selection: a larger repeated tree is not automatically a better value to materialize. A useful refinement retains common scaled-index trees, or propagates the address addition trees back into their memory uses. The calculation establishes an instruction-count improvement under the stated instruction set architecture; scheduling and actual timing depend on further machine details.
Past exam of the computer science course of the University of Oxford 2015 Compilers 3 c Solution by
Codex 0 2026-10-31
Here is a literal instruction selection for the trees in part (b), with the temporary variables allocated to processor registers.
MOV r,#k loads an arbitrary constant in one instruction. LD and ST load and store one four-byte word; [r] denotes zero-offset access. LSL is a left shift. We use conventional base-plus-immediate access for the fixed local offset I.MOV rb, #A
LD ri, [fp + I]
LSL ru, ri, #2
ADD ru, rb, ru
LD rv, [ru]
LSL rw, rv, #2
ADD rw, rb, rw
LD rt, [rw]
ST rv, [rw]
ST rt, [ru]The first four instructions construct
u, the fifth obtains v, the next two construct w, and the last three perform the remaining load and the two stores. Each destructive update of ru or rw reuses a processor register whose previous value is no longer needed. We assume enough processor registers to avoid a register spill.If the target offers only the explicitly mentioned two-register register-offset addressing and lacks base-plus-immediate access, replace the one local load by
MOV rk,#I; LD ri,[fp+rk]; all other conclusions are unchanged. A zero processor register or a zero-offset load supplies the notation [ru] and [rw] as usual. This direct translation takes ten instructions with immediate local addressing, or eleven with the stated replacement. Thus These are instruction counts for the specified abstract target, not measured execution times. Past exam of the computer science course of the University of Oxford 2015 Compilers 3 b Solution by
Codex 0 2026-10-31
The useful repeated program expressions, nested inside one another, are the memory address of
a[i], its value, and the memory address of a[a[i]]. Materialize each maximal reusable tree at the level at which it is shared, using fresh temporary variables u, v, and w:MOVE(R(u), ADD(C(A),SHL(M(ADD(R(fp),C(I))),2)))
MOVE(R(v), M(R(u)))
MOVE(R(w), ADD(C(A),SHL(R(v),2)))
MOVE(R(t), M(R(w)))
MOVE(M(R(w)), R(v))
MOVE(M(R(u)), R(t))Thus
u is a saved memory address, v the old integer value of a[i], w the second saved memory address, and t the old value at that second location. The two large address trees occur both in loads and as store destinations. After extracting them, the repeated load of a[i] inside the second address and as the middle assignment's source becomes v. Extracting every smaller nested tree again would add no benefit.The relevant alias analysis facts are that local
i does not occupy any location written through global a, that frame pointer fp and base stay fixed, and that t,u,v,w are distinct processor registers or otherwise nonoverlapping temporary variables. There are no intervening calls, volatile memory accesses, or concurrent changes. Under these assumptions, loading i once remains valid even across the array stores. Before the middle store, no array location has yet changed, so both its target memory address and its source value can be computed and saved earlier.No assumption that the two array locations are distinct is required. The middle store may change
a[i] when its target coincides with it, but v is not loaded again afterwards: only the saved address u is needed for the final store. It would be unsound to retain an arbitrary array load across an aliasing store and then treat it as the new contents; that is not what these trees do.For a language with a true global array, a distinct local variable, and no arbitrary means of fabricating pointers to unrelated storage, these facts follow simply from declarations and the stack frame layout. Accesses must denote valid array elements. In an unrestricted low-level language with overlapping storage, arbitrary pointer arithmetic or unchecked out-of-range accesses, that conclusion requires extra guarantees. Omitting emitted array bounds checks does not itself prove that all memory addresses are valid. If denotes a word load, the saved quantities are
Past exam of the computer science course of the University of Oxford 2015 Compilers 3 a Solution by
Codex 0 Created 2026-10-31 Updated 2026-11-05
Use the following explicit tree notation for the intermediate representation.
C(k) is a constant, R(r) a processor register, M(e) a word load from memory address e, ADD addition, and SHL(e,2) a left shift by two bits. MOVE(destination,source) assigns either a processor register or the computer memory word designated by M. Parentheses show complete tree structure, so these displays specify trees rather than already shared directed acyclic graphs.Let be the constant base memory address of the global array
a, and the constant offset of local i from the frame pointer fp. Each integer occupies four bytes, and array indices are zero-based; a fixed nonzero lower bound can instead be absorbed into . Local t is represented by R(t). After replacing multiplication by four with a left shift, the three trees are:MOVE(R(t),
M(ADD(C(A),
SHL(M(ADD(C(A),
SHL(M(ADD(R(fp),C(I))),2))),2))))
MOVE(M(ADD(C(A),
SHL(M(ADD(C(A),
SHL(M(ADD(R(fp),C(I))),2))),2))),
M(ADD(C(A),SHL(M(ADD(R(fp),C(I))),2))))
MOVE(M(ADD(C(A),SHL(M(ADD(R(fp),C(I))),2))),
R(t))Every
M(ADD(R(fp),C(I))) loads i; the next enclosing M loads a[i]; the outermost M in the first tree loads a[a[i]]. Each array index is converted to a byte offset by a left shift of two bits. The address rule used in all three trees is The source language's array bounds checks are omitted as requested. Past exam of the computer science course of the University of Oxford 2015 Compilers 2 f Solution by
Codex 0 2026-10-31
No: a procedural parameter can carry an older lexical environment past a newer recursive activation. Here is typed, Pascal-like pseudocode; a function-valued parameter is represented by a programming closure, comprising code and its defining environment.
procedure dummy(): integer;
begin return 0 end;
procedure P(n: integer; f: procedure(): integer): integer;
var x: integer;
procedure Q(): integer;
begin return x end;
begin
x := n;
if n = 1 then return P(2, Q)
else return f()
end;
begin print P(1, dummy) end.Call the first
P activation . It sets its own x to and passes the programming closure into a recursive activation . That activation sets its own x to and calls its parameter f. The resulting activation of Q has its static link pointing to , because that is where the passed procedure was defined. Its dynamic link points to , because that is where it was called.During this call the dynamic chain contains
Q, P2, P1, main, while Q's static chain contains Q, P1, main. The newest surviving activation of P is , but the static chain deliberately uses . Correct lexical scope therefore prints ; blindly consulting current[P] would produce . Both enclosing activations remain alive, so no issue of a returned procedure or dangling pointer is involved.The failed step in part (d) is precisely the assumption that the callee's lexical environment is a suffix of the direct caller's static chain. A passed programming closure can specify a different, still surviving environment. An implementation supporting such calls must preserve that environment; a table implementation requires additional environment installation and restoration rather than merely consulting newest activations.
Past exam of the computer science course of the University of Oxford 2015 Compilers 2 e Solution by
Codex 0 2026-10-31
Searching the dynamic chain would require identifying the procedure associated with each visited stack frame. More seriously, the search length is bounded by the number of active calls, not by lexical nesting depth. Recursion in procedures unrelated to the desired enclosing declaration can make arbitrarily large. Thus a single nonlocal variable access can take time complexity, whereas a conventional static chain access needs only traversals for lexical distance .
Instead give each procedure a known global pointer cell
current[P]. On entry to , save its previous value in the new stack frame and set current[P] to the new frame pointer. On return, restore the saved value. A nonlocal variable declared in is then loaded using current[P] and its known frame offset. The proof in part (d) justifies that lookup in the absence of procedural parameters. This is a procedure-indexed version of a display for lexical scope; a conventional display for lexical scope can instead use lexical levels as indices.With procedures and active calls, the procedure-indexed table needs additional global words and one saved previous pointer per active stack frame, hence activation storage. The saved word can replace the conventional per-frame static link if that link is no longer needed. A conventional static chain has one pointer per active stack frame and no such -entry table. Both schemes retain ordinary dynamic links and return information.
The table gives constant-time nonlocal access and constant-time save/restore work on each entry/return, independently of lexical distance. Conventional static chain access costs per access, and establishing the callee's static link may also require following an appropriate portion of the caller's chain. For a fixed program is statically bounded, but it still affects generated instruction counts. These costs assume constant-cost pointer operations and omit shared ordinary call overhead. For the procedure-indexed table,
Past exam of the computer science course of the University of Oxford 2015 Compilers 2 d Solution by
Codex 0 2026-10-31
Use mathematical induction on the sequence of call and return events. The invariant concerns the static chain of the currently executing activation: every stack frame on that chain is the most recent surviving activation of the procedure it represents. It need not hold for the saved chain of an arbitrary suspended caller while a recursive callee is running.
Initially only
main is active, so the claim holds. Suppose it holds just before a direct call to a procedure , whose lexical parent is . Without procedural parameters, visibility of means that its declaration lies in the current procedure or in a lexically enclosing procedure. Therefore the activation of needed for the call is on the caller's static chain. By the induction hypothesis, it is the newest surviving activation of .Create a new activation of whose static link points to this activation. The inherited chain is a suffix of the caller's chain, so its stack frames still represent the newest surviving activations of their procedures. Adding does not make any of those inherited activations out of date: cannot be its own lexical ancestor. The new activation itself is plainly the newest activation of . This covers recursive calls too: an older activation of may survive on the dynamic chain, but is absent from the new activation's static chain.
On return, the callee and every call it made have finished. The caller's saved stack frame and static chain are restored. Any temporarily newer activation of a procedure represented on that chain has returned, so each restored activation is again the most recent surviving one. Thus return preserves the invariant as well.
The claim holds for the executing procedure's static chain throughout every execution using direct calls under lexical scope. Equivalently, writing for the procedure represented by a stack frame ,
Past exam of the computer science course of the University of Oxford 2015 Compilers 2 c Solution by
Codex 0 2026-10-31
Continue the instruction convention above. The current stack frame is now
s's. Its static chain passes through r to p; q is declared directly inside p, so the new q activation must receive a static link to that same p stack frame. Let T be the offset of p's parameter t.Define
CALL q,1 to expect, above the pre-existing evaluation stack, the static link for q followed by its one argument. It consumes these, creates q's stack frame with its dynamic link pointing to the current s activation, and leaves one return value when q returns. This explicit convention avoids dependence on a particular real instruction set architecture.PUSHFP ; s
LOAD SL ; r
LOAD SL ; p, the required static parent of q
DUP ; preserve p for CALL while also using it to read t
LOAD T ; replace the duplicate p address by p.t
CALL q,1 ; q(p.t), with static parent p and dynamic caller s
RETVAL ; return q's result from s to rImmediately before
CALL, the evaluation stack ends in p-frame, 3. Two static link traversals find both the argument's environment and the environment to pass to q. In terms of the hidden argument and ordinary argument, An implementation that places the hidden static link after the ordinary argument could use a different ordering of these same operations. Past exam of the computer science course of the University of Oxford 2015 Compilers 2 b Solution by
Codex 0 2026-10-31
We specify a small stack machine instruction convention because the paper does not prescribe instruction spellings or numeric stack frame offsets.
PUSHFP pushes the current frame pointer. LOAD k replaces a stack-top frame address by the value at fixed offset k in that frame. Let SL be the static link offset, X the offset of p's x, and Y the offset of q's parameter y. ADD pops two integers and pushes their sum. RETVAL returns the stack-top value, removes the current activation and restores the caller through the usual calling convention.PUSHFP ; address of q's frame
LOAD SL ; q's static link: address of p's frame
LOAD X ; value of p.x
PUSHFP ; address of q's frame, above x
LOAD Y ; q's local parameter y
ADD ; x + y
RETVAL ; return the sum to sOne static link traversal suffices for
x, despite three intervening calls in the dynamic chain. y is local to q. In this execution the two loads produce and , so the returned integer is . Past exam of the computer science course of the University of Oxford 2015 Compilers 2 a Solution by
Codex 0 2026-10-31
The surviving call stack, from oldest to newest, is
main, p, r, s, q. A dynamic link identifies the caller's stack frame, whereas a static link identifies the current activation of the lexically enclosing procedure. These need not identify the same stack frame.The call stack during
. Details. q, with dynamic links on the left and static links on the right; arrows point to older stack framesThe dynamic chain isThe individual static links are
main has no enclosing procedure; its links can be null. The p stack frame contains t=3 and x=3, and the q stack frame contains y=3. Return addresses are code addresses, not links to another stack frame. In particular, q returns dynamically to s while obtaining x statically from p. Past exam of the computer science course of the University of Oxford 2015 Compilers 1 c Solution by
Codex 0 2026-10-31
Let
expr(e) generate postfix code that evaluates a program expression once, leaving one value above an otherwise unchanged evaluation stack. Let stmt(s) preserve the evaluation stack's height. Use EQ to pop two integers and push their Boolean equality result; JZ L pops that result and jumps to L when it is false. JMP is unconditional. All labels returned by fresh() are distinct, including labels inside recursively generated bodies.generateCase(Case(control, arms)):
done := fresh()
emit expr(control)
for (labelExpression, body) in arms, in source order:
next := fresh()
emit DUP
emit expr(labelExpression)
emit EQ
emit JZ next
emit POP
emit stmt(body)
emit JMP done
emit LABEL next
emit POP
emit LABEL doneTo verify the stack invariant, write the incoming evaluation stack as . Immediately before any label test it is , where is the controlling value.
DUP, evaluation of the label with value , and EQ change this to . JZ removes the Boolean. On failure the surviving is available for the next label; on success POP removes it before the body runs. The body therefore starts and ends with and jumps past every remaining label. If every label fails, the final POP restores .The controlling expression is evaluated once, each reached label is evaluated once, and no later label or arm is evaluated after a match. Keeping on the evaluation stack also preserves its original value if a label evaluation has a side effect that changes a variable appearing in the controlling program expression. A nested
Case uses the same stack invariant relative to whatever stack it receives. The concise contract is Past exam of the computer science course of the University of Oxford 2015 Compilers 1 b Solution by
Codex 0 2026-10-31
Take the lexical tokens
ID, ASSIGN, SEMI, BEGIN, END, CASE, OF, COLON, and BAR, corresponding to identifiers and the displayed punctuation or keywords. Let E be the already defined context-free grammar category for a program expression, with attribute E.tree; expressions do not consume statement delimiters except inside their own explicitly delimited constructs. ID.name is the recognized program identifier. The following context-free grammar uses S for a statement, C for a closed elementary statement, D for a nonempty sequence of closed statements, and A for a nonempty arm list. The braces in this display are semantic actions, not input lexical tokens.S -> C { S.tree = C.tree }
S -> C SEMI S1 { S.tree = Seq(C.tree, S1.tree) }
S -> CASE E OF A { S.tree = Case(E.tree, A.arms) }
C -> ID ASSIGN E { C.tree = Assign(ID.name, E.tree) }
C -> BEGIN S END { C.tree = S.tree }
D -> C { D.tree = C.tree }
D -> C SEMI D1 { D.tree = Seq(C.tree, D1.tree) }
A -> E COLON S { A.arms = [(E.tree, S.tree)] }
A -> E COLON D BAR A1 { A.arms = [(E.tree, D.tree)] ++ A1.arms }Here
++ is list concatenation, and distinct suffixes distinguish occurrences of the same nonterminal symbol. Every statement and arm body is nonempty; no empty statement or optional trailing semicolon is needed for the supplied language.The distinction between
D and S resolves both possible sources of grammar ambiguity. First, an unbracketed CASE can occur only at the right end of an S: there is no production S -> S SEMI S. Consequently all semicolon-separated statements after such a CASE enter its last arm. Enclosing that whole CASE in BEGIN ... END turns it into a C, which can be followed by another statement outside the CASE.Second, an arm followed by another arm at the same level must have a
D body. Such a body contains no unbracketed CASE. If an arm body does contain an unbracketed CASE, only the final-arm production A -> E COLON S can apply, and every following BAR before the enclosing END or end of input must be consumed by the inner CASE. Thus each bar belongs to the nearest preceding CASE whose block has not ended. A CASE inside BEGIN ... END is hidden from that rule outside its block.For uniqueness, at any statement level an initial
ID or BEGIN determines a C; an immediately following SEMI determines the recursive sequence production. An initial CASE determines the third S production. In an arm, a bar at the current block level either follows an entirely closed body, forcing the second A production, or follows an unbracketed nested CASE, forcing it into that inner arm list. With an unambiguous expression sublanguage these choices leave a unique parse tree. Closed bodies before an outer bar, and a possibly open body only in the final arm, enforce the required interpretation. Thus the context-free grammar implements Past exam of the computer science course of the University of Oxford 2015 Compilers 1 a Solution by
Codex 0 2026-10-31
Use an abstract syntax tree with the following constructors. Here
Expr denotes the separate abstract syntax tree type for a program expression, Name identifies a variable assignment target, and the square brackets denote an ordered list.Stmt = Assign(Name, Expr)
| Seq(Stmt, Stmt)
| Case(Expr, [(Expr, Stmt)])The list in
Case must be nonempty. Its first field is the controlling program expression; each pair contains a label program expression and the corresponding body. Keep the pairs in source order and keep repeated labels: this is an ordered search, with the first successful equality test choosing the body. A label is an abstract syntax tree for a program expression, not a previously evaluated integer or a key in a map data structure. Evaluating or reordering labels during compilation would change the specified programming language semantics.Seq(s,t) executes s before t. The delimiters begin and end need no surviving constructor: the parser uses them to determine the abstract syntax tree's grouping and then returns the enclosed tree. There are no declarations in these blocks that would require a separate binding environment. A list of statements could replace binary Seq; below we choose right-associated binary trees so that the semantic actions are precise.The essential representation is an ordered, nonempty list of expression/body pairs, together with assignment and sequencing nodes. In constructor notation,
Past exam of the computer science course of the University of Oxford 2015 Advanced data structures and algorithms 3 e Solution by
Codex 0 2026-10-31
Let be the given bipartite graph. A matching in a graph selects edges with no shared endpoint. For each edge , introduce an indicator for selection. The integer program for a maximum cardinality matching isIts linear programming relaxation iswith the same objective function. Explicit upper bounds may be included, but they already follow from a constraint at either endpoint.
We prove that every extreme point of is integral by showing that any feasible point with a fractional coordinate admits a two-sided perturbation. Given such an , form the fractional support graph with exactly those edges for which . This is a nonempty bipartite graph. A vertex incident to one of these edges cannot also meet an edge with value , because its incident sum would exceed .
First suppose contains a graph cycle. Choose a simple one, . Its length is even since is a bipartite graph. Define a nonzero vector by alternating along the graph cycle and putting on other edges. At each vertex on the graph cycle, the two changed coordinates have opposite signs, so its incident sum is unchanged. ChooseThen and are distinct feasible points: no coordinate leaves and all incident sums are unchanged. Since , it is not an extreme point.
Otherwise is a forest. Choose a connected component of a graph containing an edge, and a simple graph path in it between two tree leaves. Alternate along the graph path to define . Every internal vertex of the graph path still has unchanged incident sum, including any additional edges branching from it. At either endpoint, exactly one incident edge has fractional value; all other incident coordinates are , since a coordinate is impossible there. Thus each endpoint has strictly positive slack in its degree constraint. Choose smaller than these two slacks and smaller than every and along the graph path. Both and remain feasible: the endpoint sums can increase or decrease by , and the internal sums do not change. Again is the midpoint of distinct feasible points, contradicting extremality.
Both cases exclude fractional coordinates at an extreme point. ThereforeThe nonempty bounded linear polyhedron has an optimal extreme point for the stated linear function, so the relaxation yields a maximum cardinality matching. This is stronger than the existence of one integral optimum: it proves integrality of every extreme point. It does not claim that every optimal feasible point is integral; convex mixtures of distinct optimal matchings in a graph can be fractional.
Past exam of the computer science course of the University of Oxford 2015 Advanced data structures and algorithms 3 d Solution by
Codex 0 2026-10-31
A feasible point is an extreme point solution of a linear program if it cannot be written as a nontrivial convex combination of two distinct feasible points. Explicitly, ifthen . Equivalently, lies in no open line segment joining two distinct feasible points. Being an extreme point concerns the feasible region; it does not by itself mean that optimizes the objective function.
Past exam of the computer science course of the University of Oxford 2015 Advanced data structures and algorithms 3 c Solution by
Codex 0 2026-10-31
Assume job sizes and machine speeds , with at least one machine. Let be the amount of job assigned to machine , and let be the proposed makespan. Use the linear programThe first family of constraints assigns every job completely. The second bounds each machine's completion time by , since is equivalent to . It is a linear inequality because is an input constant. There is no integrality restriction: jobs are divisible as specified in this scheduling model.
To verify the formulation, any actual allocation defines the variables above, with equal to its largest completion time, and satisfies every constraint. Conversely, from a feasible solution, assign amount to machine and process its assigned pieces consecutively. Its total time is at most , so the linear program realizes the claimed makespan.
Past exam of the computer science course of the University of Oxford 2015 Advanced data structures and algorithms 3 b Solution by
Codex 0 2026-10-31
A vertex cover of an undirected graph is a subset containing at least one endpoint of every edge. The optimization version of the vertex cover problem minimizes ; the vertex cover decision problem asks whether a cover of size at most a given bound exists.
Let mean that vertex is selected. An integer program for minimum-size vertex cover isThe linear programming relaxation replaces by . The edge inequalities enforce coverage, and the objective function counts selected vertices in the integer program.
For a graph triangle with vertices , the three covering constraints areAdding them gives . Therefore the relaxed objective function is at least . The feasible solution attains that lower bound, soIn contrast, one vertex leaves the opposite edge uncovered and any two vertices cover all three edges, giving integer optimum . The example exhibits a genuinely non-integer optimal value, rather than merely a fractional feasible solution.
Past exam of the computer science course of the University of Oxford 2015 Advanced data structures and algorithms 3 a Solution by
Codex 0 2026-10-31
Using the standard maximum form of a linear program, writewhere , , , and . The inequalities are coordinatewise, and is the objective function. General linear programs can be put in this form: reverse the sign of a minimization objective function, reverse lower-bound linear inequalities, replace an equality by two opposite linear inequalities, and replace each unrestricted variable by the difference of two nonnegative variables. Some conventions instead call the equality-constraint version standard form; the convention here is the maximum-with-upper-bounds convention.
Introduce nonnegative slack variables . The equivalent slack form of a linear program isThe original variables are initially nonbasic and the slack variables basic. More generally, a slack form of a linear program specifies disjoint index sets and writesThus each basic variable and the objective function are expressed using only nonbasic variables. Setting all nonbasic variables to zero gives the associated basic solution, which is a feasible solution exactly when the resulting basic values are nonnegative. The slack form of a linear program does not itself require that initial basic solution to be feasible.
Past exam of the computer science course of the University of Oxford 2015 Advanced data structures and algorithms 2 d Solution by
Codex 0 2026-10-31
For each user define its set of eligible ads byThis uses the intersection of demographic-group memberships: belonging to one advertised group suffices, because the eligibility condition is a set union. A user in several groups still receives at most one ad.
A direct algorithm is to process users independently. For user , scan ads until finding one for which some group belongs to both and . Assign that ad and stop processing this user. If no such ad exists, leave the user unassigned. There is no upper bound on the number of different users who can receive the same ad, so these decisions do not compete for an ad resource.
For correctness, every produced impression has a shared permitted group, and the stopping rule assigns at most one ad to each user. Conversely, a user with cannot receive an ad in any feasible solution. Since every other user can contribute at most one impression, every feasible solution has value at mostThe algorithm attains this bound, provingRepresent each membership set by a length- array of Boolean variables. Testing one user-ad pair costs , so all tests and input setup take time in Big O notation, which is polynomial time in all three parameters. For positive , this simplifies to .
A maximum flow formulation gives the same result if desired: add edges with flow network edge capacity , with flow network edge capacity exactly when , and with flow network edge capacity . By the integral max-flow theorem, an integral maximum flow selects valid impressions. Setting the last flow network edge capacity to would impose an extra restriction absent from this paper.
There are unlisted articles, also show them or only show them.
