Teaser 2344: [Football league]
From The Sunday Times, 26th August 2007 [link]
The Borsetshire League has 20 teams. After each had played one match, I looked at the league table. Teams were in order, and no two were level (positions decided by points won, then goal difference in the event of ties, then goals scored if they still tied). The top team had not played the bottom team. And, though Perks had scored a hat-trick, his team, Ambridge, were in the bottom half. If you knew the total number of goals scored, you could work out all the scores.
What, in order, were the scores in the matches played by the 10 leading teams?
This puzzle was originally published with no title.
[teaser2344]








Jim Randell 10:03 am on 29 October 2026 Permalink |
There are 20 teams, and each has played exactly 1 match, so there have been 10 matches played. And no match can have been drawn, as we cannot differentiate between the teams that played in a drawn match.
So we are looking for 10 matches that are won/lost. The teams that won are in the top half, and the teams that lost are in the bottom half. And one of the teams that lost scored (at least) 3 goals (so there must have been at least 7 goals scored in that match).
This Python 3 program considers possible increasing total numbers of goals scored in the tournament. It then divides the total number of goals among the 10 matches, and allocates different score lines for each match, and looks to see if they make a valid order. If there is only a single valid order, then we have found a solution to the puzzle.
It runs in 165ms. (Internal runtime is 95ms).
from enigma import ( irange, inf, decompose, multiset, subsets, rev, singleton, ulambda, cache, printf ) # return possible matches with <n> goals scored @cache def matches(n): return list(decompose(n, 2, increasing=-1, sep=1, min_v=0)) # choose matches for the totals given in multiset <m> def choose(m, rs=list()): # are we done? if not m: yield rs else: # allocate different matches for the next goal total k = m.peek() for ms in subsets(matches(k), size=m.count(k)): yield from choose(m.copy().discard(k), rs + list(ms)) # sort matches according to (goal difference), then (goals scored) order = ulambda("(x, y): (x - y, x)") # find viable orders for a given total number of goals <t> def solve(t): # decompose the total into 10 matches for ms in decompose(t, 10, increasing=1, sep=0, min_v=1): # one of the matches must have at least 7 goals scored if max(ms) < 7: continue # all matches must be different m = multiset.from_seq(ms) if any(v > len(matches(k)) for (k, v) in m.items()): continue # choose winning matches for each total for ws in choose(m): # there must be a team that lost with at least 3 goals if not any(y >= 3 for (x, y) in ws): continue # each match won is also lost ms = list(ws) ms.extend((y, x) for (x, y) in ws) # put the matches in order ss = sorted(ms, key=order, reverse=1) # the top team did not play the bottom team if ss[0] == rev(ss[-1]): continue # valid order yield ss # consider possible total goals scored in the 10 matches for t in irange(10, inf): # is there a single viable order? ss = singleton(solve(t)) if ss: printf("{t} total goals") printf("-> {ss}") breakSolution: The scores (in order) of the matches played by the top 10 teams was:
There were 40 goals scored in the tournament.
The bottom half of the table looks like this (scores from the losing teams perspective):
We see that the top team did not play the bottom team. The matches are (1st vs 19th) and (2nd vs 20th), and the 11th team lost (to the 7th), even though they had scored 3 goals.
LikeLike