Sunday Times Teaser 2516 – No Title
by Nick MacKinnon
Published December 12 2010 (link)
Six teams — Ayes, Bees, Cees, Dees, Ease and Fees — played each other once in a football league, with three points for a win and one for a draw. Ayes finished top, with the order of the teams being alphabetical. The difference in points between each pair of adjacent teams was the same. The 13 goals scored included hattricks from the Dees striker in two matches. In one game, the Cees goalkeeper let in two before being substituted.
What were Cees’ five results (in the order CvA, CvB, CvD, CvE, CvF)?
One Comment
Leave one →

Brian Gladman permalinkPython123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152from itertools import productfrom functools import reducefrom collections import namedtuple# partition an integer <x> into <n> parts with all permutations# of the resulting partsdef partition(x, n, seq=[]):if n == 1:yield tuple(seq + [x])else:for i in range(x + 1):yield from partition(x  i, n  1, seq + [i])# compile a table of results for a side given the results of their# matches (loss, draw, win) in <result>; the corresponding item in# <reverse> indicate if the team is 1st (0) or 2nd (1) in the given# results; the table gives the number of losses, draws and wins,# the number of games played, the points won and the match resultsdef table(result, reverse):ldw, t_res = [0] * 6, tuple()for res, rev in zip(result, reverse):if rev:res = (win_pts if not res else 1 if res == 1 else 0)t_res += (res,)ldw[res] += 1ldw[4] += 1ldw[5] += resdel ldw[2 if win_pts == 3 else 3]ldw.append(t_res)tb = namedtuple('football_table', 'losses, draws, wins, played, points matches')return tb._make(ldw)# given the list of (loss, draw, win) match results for a side in# <res>, generate match results in goals that are consistent with# the loss, draw, win results and the side's 'goals for' and 'goals# against' values; each value in <rev> applies to the corresponding# item in <res> and is (0, 1) depending on whether the side is the# 1st or 2nd team in the given result; the same is true for <srv># in relation to <scores>, where the latter lists any of the sides# results (in goals) that are already known (unknowns are zero)def matches(res, scores, srv, gf, ga):# tp holds the loss, draw, win results for the undetermined# matches (ix holds these match positions in <scores>)tp, ix = [], []for i, (r, sc, sv) in enumerate(zip(res, scores, srv)):if not sc:# reverse the loss, draw, win result if necessarytp.append(r)# record the empty position in <sores>ix.append(i)else:# if a match result in goals is known, reverse it if necessary# and remove its goals from the goals for and against totalst = (sc[1], sc[0]) if sv else scscores[i] = tgf = t[0]ga = t[1]if not tp:yield scoreselse:# distribute the 'goals for' among the undetermined matchesfor aa in partition(gf, len(tp)):# ... and the 'goals against'for bb in partition(ga, len(tp)):# check that the loss, draw, win results match the goals scoredif all(x == 0 and y < z or x == 1 and y == z or x == win_pts and y > zfor x, y, z in zip(tp, aa, bb)):# return the results in goals for each match playedfor i, a, b in zip(ix, aa, bb):scores[i] = (a, b)yield scores# points for a loss, a draw and a winwin_pts = 3points = 0, 1, win_pts# 13 goals in 15 games, but two games have at least three goals# so there are a maximum of 7 goals in 13 games; hence there is a# minimum of 6 noscore draws and 9 wins giving a maximum of 39# points; if F has f points and the points difference between# adjacent table places is i, we have 6.f + 15.i < 40; also D# has at least 6 points so f + 2.i >= 6; the only solution is# f = 4 and i = 1 with the maximum 39 points in total; so there# are 6 noscore draws and 9 wins, two of which are (3, 0); the# remaining seven  with 7 goals between them  must be (1, 0)sol = []# consider possible points scored in each of A's gamesfor AB, AC, AD, AE, AF in product(points, repeat=5):At = table([AB, AC, AD, AE, AF], [0, 0, 0, 0, 0])if At.points != 9:continuefor BC, BD, BE, BF in product(points, repeat=4):Bt = table([AB, BC, BD, BE, BF], [1, 0, 0, 0, 0])if Bt.points != 8:continuefor CD, CE, CF in product(points, repeat=3):Ct = table([AC, BC, CD, CE, CF], [1, 1, 0, 0, 0])if Ct.points != 7:continuefor DE, DF in product(points, repeat=2):Dt = table([AD, BD, CD, DE, DF], [1, 1, 1, 0, 0])if Dt.points != 6:continuefor EF in points:Et = table([AE, BE, CE, DE, EF], [1, 1, 1, 1, 0])if Et.points != 5:continueFt = table([AF, BF, CF, DF, EF], [1, 1, 1, 1, 1])if Ft.points != 4:continuesol.append((At, Bt, Ct, Dt, Et, Ft))def outp(*arg):print(' '.join(str(x) if x != None else '' for x in arg))for At, Bt, Ct, Dt, Et, Ft in sol:# consider D's matches first since they score the most goalsfor da, db, dc, de, df in matches(Dt.matches, [0, 0, (3, 0), 0, 0],[0, 0, 0, 0, 0], Dt.wins + 4, Dt.losses):# goals against a team equal their number of lost matches except when# a match is lost against D when the goals against is increased by two# because their wins are with three goals raather than onega_A, ga_B, ga_C, ga_E, ga_F = (x.losses + (2 if Dt.matches[i] == 3 else 0)for i, x in enumerate((At, Bt, Ct, Et, Ft)))for ca, cb, cd, ce, cf in matches(Ct.matches, [0, 0, dc, 0, 0],[0, 0, 1, 0, 0], Ct.wins, ga_C):for ab, ac, ad, ae, af in matches(At.matches, [0, ca, da, 0, 0],[0, 1, 1, 0, 0], At.wins, ga_A):for ba, bc, bd, be, bf in matches(Bt.matches, [ab, cb, db, 0, 0],[1, 1, 1, 0, 0], Bt.wins, ga_B):for ea, eb, ec, ed, ef in matches(Et.matches, [ae, be, ce, de, 0],[1, 1, 1, 1, 0], Et.wins, ga_E):for fa, fb, fc, fd, fe in matches(Ft.matches, [af, bf, cf, df, ef],[1, 1, 1, 1, 1], Ft.wins, ga_F):print(' A B C D E F')outp('A: ', None, ab, ac, ad, ae, af)outp('B: ', ba, None, bc, bd, be, bf)outp('C: ', ca, cb, None, cd, ce, cf)outp('D: ', da, db, dc, None, de, df)outp('E: ', ea, eb, ec, ed, None, ef)outp('F: ', fa, fb, fc, fd, fe, None)