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.Input
The first line holds two integers,
NandT. The second line holds theNcard values, separated by spaces.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 isNONE.Sample Input
6 9 4 5 1 8 3 6Sample Output
3 4 5The three pairs are
4 5,1 8and3 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 9into 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()andint(), for a fixed count of values or until a0arrives. -
Keep pairs together as tuples, and solve small problems by complete search: try every candidate or every pair.
-
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.
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.
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.
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.
-
Exact answers
Predict what each line prints, then check your prediction against the output.
print(3, 4) print("3,4") print("The answer is", 3, 4)Scroll the code for more ↓
Output
3 4 3,4 The answer is 3 4printputs one space between the things you give it. If the answer should be3 4, then3,4is wrong, and so isThe answer is 3 4. A judge compares your output with the expected output, word for word, so print only what the problem asks for.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.
x = 7 y = x // 2 x = x + y if x > 9: y = y * 10 print(x, y)Scroll the code for more ↓
Output
10 30Tracing means keeping a small table of every variable and updating it one line at a time. Here
ybecomes3, thenxbecomes10, so theifruns andybecomes30. The exact answer is10 30.Reading a problem statement
Look back at the problem at the top of this unit. Every ACSL-style programming statement has the same parts.
-
The story: what is being asked, in words.
-
Input: exactly what arrives, line by line.
-
Output: exactly what to print, line by line, and what to print in the special case (here,
NONE). -
Sample: one input with its correct output. Work it by hand before you code.
-
- Constraints: how big the numbers can get. They tell you which edge cases can happen (can
Nbe 1? can values be negative?).
Read all five parts before you write any code.
- Constraints: how big the numbers can get. They tell you which edge cases can happen (can
Running a program with input
A contest program does not ask questions with a prompt like
"Enter a number: ". It callsinput()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.line = "12345" total = 0 for digit in line: total = total + int(digit) print(total)Scroll the code for more ↓
Output
15A line of text is a string, and a
forloop walks through it one character at a time. Each character is still text, soint(digit)turns"3"into3before adding it.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 withinput(). It is saved asassets/l1.py.Try it line = input() total = 0 for digit in line: total = total + int(digit) print(total)Scroll the code for more ↓
To run it, open a terminal in this unit's folder and type:
python assets/l1.pyThe program waits. Type
12345and press Enter; it prints15.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.inThe file
assets/l1/1.inholds the input, andassets/l1/1.outholds the expected output. A judge does exactly this: it runs your program on each input file and compares what you print with the.outfile.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.Try it line = input() print(line)Scroll the code for more ↓
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 forinput().Splitting a line
.split()breaks a string wherever there are spaces and gives back a list of strings.line = "17 5" parts = line.split() print(parts) print(len(parts))Scroll the code for more ↓
Output
['17', '5'] 2line = " ACSL is fun " print(line.split())Scroll the code for more ↓
Output
['ACSL', 'is', 'fun'].split()ignores extra spaces, including spaces at the start and end. You always get just the words or numbers.Turning pieces into numbers
The pieces are still text. Adding two text pieces glues them together; adding two numbers adds them.
parts = "40 2".split() print(parts[0] + parts[1]) a = int(parts[0]) b = int(parts[1]) print(a + b)Scroll the code for more ↓
Output
402 42"40" + "2"is"402", but40 + 2is42. Callint(...)on every piece that is a number before you do arithmetic with it. Forgetting this is one of the most common contest mistakes.A line can mix words and numbers. Keep the word as a string and convert only the number.
line = "Ada 95" parts = line.split() name = parts[0] score = int(parts[1]) print(name, score + 5)Scroll the code for more ↓
Output
Ada 100Several 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:Try it name = input() parts = input().split() width = int(parts[0]) height = int(parts[1]) print(name, width * height)Scroll the code for more ↓
input().split()reads a line and splits it in one step.Reading a fixed count
Many problems start with a count
Nand then giveNmore lines. ReadNfirst, then loopNtimes. To try the idea here, we keep the lines of an input in a list and walk through it; the real program callsinput()instead.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)Scroll the code for more ↓
Output
Ada CyLine 0 holds the count, so the student lines are at positions 1 to
n, which is why the loop readslines[k + 1].The same program, reading real input:
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)Scroll the code for more ↓
Sometimes all
Nvalues sit on one line after the count. Then you split that one line and convert each piece.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))Scroll the code for more ↓
Output
[8, 3, 12, 5] 12 3Build a list of integers first; then
max,min, loops and comparisons all work on real numbers.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.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)Scroll the code for more ↓
Output
3The
0stops the loop and is never counted. If the first value is already0, the loop body never runs and the count is0.Put it together: this program reads integers, one per line, until it reads
0. It prints how many values came before the0, their total, and the largest of them. It is saved asassets/l2.py.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)Scroll the code for more ↓
Run it from this unit's folder:
python assets/l2.py < assets/l2/1.inlargestis set from the first value, not started at0. Test it onassets/l2/2.in, where every value is negative: a program that startslargestat0would wrongly print0.ACSL input in this book is always integers and words. You never need decimals, so every number you read goes through
int(...).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.
Tuples
A tuple is written with round brackets and commas. You read its parts by position, just like a list.
point = (3, -4) print(point) print(point[0], point[1])Scroll the code for more ↓
Output
(3, -4) 3 -4Unpacking puts the parts of a tuple into separate variables in one line.
point = (3, -4) x, y = point print(x) print(y)Scroll the code for more ↓
Output
3 -4There 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] = 5is 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 examplepoint = (5, -4).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.a = 1 b = 2 a, b = b, a print(a, b)Scroll the code for more ↓
Output
2 1Lists of tuples
A list can hold tuples, and a
forloop can unpack each one as it goes.points = [] points.append((2, 5)) points.append((-1, 3)) points.append((4, -6)) for x, y in points: print(x + y)Scroll the code for more ↓
Output
7 2 -2points.append((2, 5))has two sets of brackets: the outer ones belong toappend, the inner ones make the tuple.One more built-in helps with points:
abs(n)gives the size ofnwithout its minus sign, soabs(-6)is6andabs(4)is4.point = (4, -6) x, y = point print(abs(x) + abs(y))Scroll the code for more ↓
Output
10Complete 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
xandy, each from 0 to 10, make3x + 5yequal to 31?answers = [] for x in range(11): for y in range(11): if 3 * x + 5 * y == 31: answers.append((x, y)) print(answers)Scroll the code for more ↓
Output
[(2, 5), (7, 2)]The nested loops try all 11 × 11 = 121 combinations, and each answer is stored as a tuple
(x, y).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.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])Scroll the code for more ↓
Output
0 1 9 0 2 5 0 3 12 1 2 6 1 3 13 2 3 94 cards give 6 pairs. With
Ncards there areN × (N − 1) / 2pairs; forN = 1000that is 499,500, which a computer checks in a moment.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.
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)Scroll the code for more ↓
Output
3 (-7, -8)beststarts 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.Put it together: here is a complete solution to the problem at the top of the unit. It reads
NandT, reads the cards, tries every pair, counts the pairs that add up toT, and keeps the first one as a tuple. It is saved asassets/l3.py.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)Scroll the code for more ↓
Run it from this unit's folder:
python assets/l3.py < assets/l3/1.inThe
if count == 1test saves only the first pair found; later pairs are counted but not saved. Becauseigrows in the outer loop andjin 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.inprints0andNONE), 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, andint()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)withfor 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.
-
You can now read a contest problem's input, search every possibility, and print the exact answer it asks for.