Exit slides Esc

Unit 0 — ACSL Foundations

Slide 1 of 69

  1. The problem: Pairs That Make the Target

    This is the kind of programming problem you meet on an ACSL contest. Read it now; by the end of this unit you will write a program that solves it.

    A row of number cards lies on the table. Two cards at different positions form a pair. How many pairs add up to exactly the target number T? Also report the first such pair: the one whose first card is furthest to the left, and, among those, whose second card is furthest to the left.

  2. Input

    The first line holds two integers, N and T. The second line holds the N card values, separated by spaces.

  3. Output

    Print two lines. The first line is the number of pairs whose values add up to T. The second line is the first such pair, as its two values in left-to-right order, separated by one space. If there is no such pair, the second line is NONE.

  4. Sample Input

    6 9
    4 5 1 8 3 6
  5. Sample Output

    3
    4 5

    The three pairs are 4 5, 1 8 and 3 6.

  6. Constraints

    • 1 <= N <= 1000

    • Every card value is between -1000 and 1000.

    • -2000 <= T <= 2000

    To solve it you need three skills, one per lesson: how an ACSL contest works and how a program is judged (Lesson 1), how to read input like 6 9 into numbers (Lesson 2), and how to try every pair and keep the one you want (Lesson 3).

    • How ACSL works: its divisions, its four contests, and its two kinds of problems (short answers and programs).

    • Read a contest problem statement, and run a program with input you type or read from a file with <.

    • Read contest input with input(), split() and int(), for a fixed count of values or until a 0 arrives.

    • Keep pairs together as tuples, and solve small problems by complete search: try every candidate or every pair.

  7. Lesson 1 — How ACSL works

    The American Computer Science League (ACSL) runs a contest season for school students. A season has four contests, spread over the school year, from fall to spring. You take each contest at your school, during that contest's window.

  8. Each contest tests a few topics, which ACSL calls categories. For example, Contest 1 includes Computer Number Systems and Recursive Functions. This book follows the season: after this Foundations unit come four parts, Contest 1 to Contest 4, each with one unit per category and a practice contest at the end. Study each part in the weeks before that contest.

  9. The divisions

    ACSL has five divisions.

    Division Who it is for Programming problem?
    Elementary younger students; one category per contest no
    Classroom a whole class; questions from several categories no
    Junior mostly middle-school students yes
    Intermediate high-school students yes
    Senior experienced high-school students; the hardest division yes

    Junior, Intermediate and Senior are a ladder: each step is harder than the one before. Every exercise in this book is marked with the lowest division it belongs to. If you are preparing for Junior, do the Junior items; an Intermediate student does the Junior and Intermediate items, and so on. Foundations starts at Junior, because it is about programming.

  10. Two kinds of problems

    A Junior, Intermediate or Senior contest has two parts.

    • Short-answer questions. You work with pencil and paper and write one answer for each question. A typical one says: "Given this input, what exactly does this program print?" ACSL calls these What Does This Program Do? questions.

    • One programming problem. You write a program that reads input and prints output. It is judged on several test inputs, and each test counts only if your output is exactly right.

    Both parts want exact answers. Close does not count.

  11. Exact answers

    Predict what each line prints, then check your prediction against the output.

  12. print(3, 4)
    print("3,4")
    print("The answer is", 3, 4)

    Output

    3 4
    3,4
    The answer is 3 4
    

    print puts one space between the things you give it. If the answer should be 3 4, then 3,4 is wrong, and so is The answer is 3 4. A judge compares your output with the expected output, word for word, so print only what the problem asks for.

  13. A short-answer question works the same way. Trace this program by hand, write down what you think it prints, and only then run it.

  14. x = 7
    y = x // 2
    x = x + y
    if x > 9:
        y = y * 10
    print(x, y)

    Output

    10 30
    

    Tracing means keeping a small table of every variable and updating it one line at a time. Here y becomes 3, then x becomes 10, so the if runs and y becomes 30. The exact answer is 10 30.

  15. Reading a problem statement

    Look back at the problem at the top of this unit. Every ACSL-style programming statement has the same parts.

    1. The story: what is being asked, in words.

    2. Input: exactly what arrives, line by line.

    3. Output: exactly what to print, line by line, and what to print in the special case (here, NONE).

    4. Sample: one input with its correct output. Work it by hand before you code.

    1. Constraints: how big the numbers can get. They tell you which edge cases can happen (can N be 1? can values be negative?).

    Read all five parts before you write any code.

  16. Running a program with input

    A contest program does not ask questions with a prompt like "Enter a number: ". It calls input() with nothing in the brackets and reads the line the judge gives it.

    Here is a small complete program. It reads one line holding a whole number, such as 12345, and prints the sum of its digits.

  17. line = "12345"
    total = 0
    for digit in line:
        total = total + int(digit)
    print(total)

    Output

    15
    

    A line of text is a string, and a for loop walks through it one character at a time. Each character is still text, so int(digit) turns "3" into 3 before adding it.

  18. The example above writes the line into the code as line = "12345", so that it runs without anyone typing the input. The real program reads the line with input(). It is saved as assets/l1.py.

  19. Try it
    line = input()
    total = 0
    for digit in line:
        total = total + int(digit)
    print(total)
  20. To run it, open a terminal in this unit's folder and type:

    python assets/l1.py

    The program waits. Type 12345 and press Enter; it prints 15.

    You can also feed it a file instead of typing. The < sign sends the file's contents to the program as if you had typed them:

    python assets/l1.py < assets/l1/1.in

    The file assets/l1/1.in holds the input, and assets/l1/1.out holds the expected output. A judge does exactly this: it runs your program on each input file and compares what you print with the .out file.

  21. Lesson 2 — Reading contest input

    Every call to input() reads one whole line and gives it back as a string, without the Enter at the end. Contest input is made of integers and words, so reading it is always the same three steps: read a line, split it into pieces, and turn the number pieces into integers.

  22. Try it
    line = input()
    print(line)

    This program waits for someone to type a line, so it is shown here without any output. In the rest of this lesson we store a line in a variable, such as line = "17 5", to stand in for input().

  23. Splitting a line

    .split() breaks a string wherever there are spaces and gives back a list of strings.

  24. line = "17 5"
    parts = line.split()
    print(parts)
    print(len(parts))

    Output

    ['17', '5']
    2
    
  25. line = "  ACSL   is    fun  "
    print(line.split())

    Output

    ['ACSL', 'is', 'fun']
    

    .split() ignores extra spaces, including spaces at the start and end. You always get just the words or numbers.

  26. Turning pieces into numbers

    The pieces are still text. Adding two text pieces glues them together; adding two numbers adds them.

  27. parts = "40 2".split()
    print(parts[0] + parts[1])
    a = int(parts[0])
    b = int(parts[1])
    print(a + b)

    Output

    402
    42
    

    "40" + "2" is "402", but 40 + 2 is 42. Call int(...) on every piece that is a number before you do arithmetic with it. Forgetting this is one of the most common contest mistakes.

  28. A line can mix words and numbers. Keep the word as a string and convert only the number.

  29. line = "Ada 95"
    parts = line.split()
    name = parts[0]
    score = int(parts[1])
    print(name, score + 5)

    Output

    Ada 100
    
  30. Several lines

    Each call to input() reads the next line. A program that reads a name on the first line and two numbers on the second looks like this:

  31. Try it
    name = input()
    parts = input().split()
    width = int(parts[0])
    height = int(parts[1])
    print(name, width * height)

    input().split() reads a line and splits it in one step.

  32. Reading a fixed count

    Many problems start with a count N and then give N more lines. Read N first, then loop N times. To try the idea here, we keep the lines of an input in a list and walk through it; the real program calls input() instead.

  33. lines = ["3", "Ada 95", "Ben 78", "Cy 86"]
    n = int(lines[0])
    for k in range(n):
        parts = lines[k + 1].split()
        name = parts[0]
        score = int(parts[1])
        if score >= 80:
            print(name)

    Output

    Ada
    Cy
    

    Line 0 holds the count, so the student lines are at positions 1 to n, which is why the loop reads lines[k + 1].

  34. The same program, reading real input:

  35. Try it
    n = int(input())
    for k in range(n):
        parts = input().split()
        name = parts[0]
        score = int(parts[1])
        if score >= 80:
            print(name)
  36. Sometimes all N values sit on one line after the count. Then you split that one line and convert each piece.

  37. first = "4"
    second = "8 3 12 5"
    n = int(first)
    parts = second.split()
    values = []
    for position in range(n):
        values.append(int(parts[position]))
    print(values)
    print(max(values), min(values))

    Output

    [8, 3, 12, 5]
    12 3
    

    Build a list of integers first; then max, min, loops and comparisons all work on real numbers.

  38. Reading until 0

    Some problems do not tell you the count. Instead, the input ends with a special value, often 0, that is not part of the data. This is the "repeat until done" loop from your Python book: read one value before the loop, and read the next one at the end of the loop.

  39. lines = ["12", "7", "5", "0"]
    position = 0
    value = int(lines[position])
    count = 0
    while value != 0:
        count = count + 1
        position = position + 1
        value = int(lines[position])
    print(count)

    Output

    3
    

    The 0 stops the loop and is never counted. If the first value is already 0, the loop body never runs and the count is 0.

  40. Put it together: this program reads integers, one per line, until it reads 0. It prints how many values came before the 0, their total, and the largest of them. It is saved as assets/l2.py.

  41. Try it
    count = 0
    total = 0
    largest = 0
    value = int(input())
    while value != 0:
        if count == 0:
            largest = value
        if value > largest:
            largest = value
        count = count + 1
        total = total + value
        value = int(input())
    print(count, total, largest)
  42. Run it from this unit's folder:

    python assets/l2.py < assets/l2/1.in
  43. largest is set from the first value, not started at 0. Test it on assets/l2/2.in, where every value is negative: a program that starts largest at 0 would wrongly print 0.

  44. ACSL input in this book is always integers and words. You never need decimals, so every number you read goes through int(...).

  45. Lesson 3 — Tuples and complete search

    The target problem asks for a pair of cards. Python has a neat way to keep two values together: a tuple.

  46. Tuples

    A tuple is written with round brackets and commas. You read its parts by position, just like a list.

  47. point = (3, -4)
    print(point)
    print(point[0], point[1])

    Output

    (3, -4)
    3 -4
    
  48. Unpacking puts the parts of a tuple into separate variables in one line.

  49. point = (3, -4)
    x, y = point
    print(x)
    print(y)

    Output

    3
    -4
    

    There must be exactly as many names on the left as parts in the tuple.

    Unlike a list, a tuple cannot be changed after it is made: point[0] = 5 is an error. That is what you want for a pair of values that belong together, such as the two cards of an answer. To "change" it, you make a new tuple, for example point = (5, -4).

  50. A comma on the right-hand side also makes a tuple. That gives a one-line swap: Python builds the tuple (b, a) from the old values first, then unpacks it.

  51. a = 1
    b = 2
    a, b = b, a
    print(a, b)

    Output

    2 1
    
  52. Lists of tuples

    A list can hold tuples, and a for loop can unpack each one as it goes.

  53. points = []
    points.append((2, 5))
    points.append((-1, 3))
    points.append((4, -6))
    for x, y in points:
        print(x + y)

    Output

    7
    2
    -2
    

    points.append((2, 5)) has two sets of brackets: the outer ones belong to append, the inner ones make the tuple.

    One more built-in helps with points: abs(n) gives the size of n without its minus sign, so abs(-6) is 6 and abs(4) is 4.

  54. point = (4, -6)
    x, y = point
    print(abs(x) + abs(y))

    Output

    10
    
  55. Complete search: try every candidate

    Many contest problems are small enough that you can simply try every possibility and check each one. This is called complete search (or brute force). Before you look for anything clever, ask: "How many candidates are there? Can I just try them all?"

    Example: which whole numbers x and y, each from 0 to 10, make 3x + 5y equal to 31?

  56. answers = []
    for x in range(11):
        for y in range(11):
            if 3 * x + 5 * y == 31:
                answers.append((x, y))
    print(answers)

    Output

    [(2, 5), (7, 2)]
    

    The nested loops try all 11 × 11 = 121 combinations, and each answer is stored as a tuple (x, y).

  57. Every pair of positions

    To try every pair of cards, use two loops over positions. The inner loop starts at i + 1, so each pair is tried exactly once and a card is never paired with itself.

  58. values = [4, 5, 1, 8]
    for i in range(len(values)):
        for j in range(i + 1, len(values)):
            print(i, j, values[i] + values[j])

    Output

    0 1 9
    0 2 5
    0 3 12
    1 2 6
    1 3 13
    2 3 9
    

    4 cards give 6 pairs. With N cards there are N × (N − 1) / 2 pairs; for N = 1000 that is 499,500, which a computer checks in a moment.

  59. Counting and choosing the best

    Complete search usually does one of two things with the candidates: count the ones that pass a test, or keep the best one. A tuple is a good way to remember the best candidate found so far.

  60. values = [4, -7, 5, 1, -8]
    count = 0
    best = (values[0], values[1])
    for i in range(len(values)):
        for j in range(i + 1, len(values)):
            if values[i] + values[j] > 0:
                count = count + 1
            if values[i] * values[j] > best[0] * best[1]:
                best = (values[i], values[j])
    print(count)
    print(best)

    Output

    3
    (-7, -8)
    
  61. best starts as a real pair (the first two cards), never as a made-up value, so it works even when every product is negative. The winning product here is -7 × -8 = 56: two negatives beat the positive pairs.

  62. Put it together: here is a complete solution to the problem at the top of the unit. It reads N and T, reads the cards, tries every pair, counts the pairs that add up to T, and keeps the first one as a tuple. It is saved as assets/l3.py.

  63. Try it
    first = input().split()
    n = int(first[0])
    target = int(first[1])
    parts = input().split()
    values = []
    for position in range(n):
        values.append(int(parts[position]))
    count = 0
    found = (0, 0)
    for i in range(n):
        for j in range(i + 1, n):
            if values[i] + values[j] == target:
                count = count + 1
                if count == 1:
                    found = (values[i], values[j])
    print(count)
    if count == 0:
        print("NONE")
    else:
        a, b = found
        print(a, b)
  64. Run it from this unit's folder:

    python assets/l3.py < assets/l3/1.in
  65. The if count == 1 test saves only the first pair found; later pairs are counted but not saved. Because i grows in the outer loop and j in the inner loop, the first pair found is the leftmost one, exactly as the problem defines "first".

    Before you trust a solution, test the edges the constraints allow: no pair at all (assets/l3/2.in prints 0 and NONE), every card the same (assets/l3/3.in), and negative values (assets/l3/4.in).

    • An ACSL answer must match exactly, so predict each printed line before you run it.

    • input().split() breaks a line into pieces, and int() turns each piece into a number.

    • When the count is not given, keep reading until the stop value, such as 0.

    • for i in range(n) with for j in range(i + 1, n) visits every pair of positions exactly once.

    • Complete search: try every candidate, count the ones that work, and keep the best one.

  66. You can now read a contest problem's input, search every possibility, and print the exact answer it asks for.