# Who marks whom? The assignment problem at set pieces

Source: https://www.footballdatascience.co.uk/learn/assignment-problem-set-pieces
Published: 2026-10-01

> Five markers, five attackers at a corner. Putting your best header on their most dangerous man sounds right and concedes more. The assignment problem finds the best set of pairings, the Hungarian method solves it by hand, and 190 real corners a season turn the difference into goals.

**On the terraces:** Put your best header on their most dangerous man? It sounds right, and it can cost you. This piece shows how to work out who should mark whom at corners, so the whole set of pairings works, not just the first one.

## The football question

A corner against. The other side sends five into the box: a target man, their centre-back, a near-post runner, a late arrival from the edge and their striker. You have five markers. **Who should pick up whom?**

The instinct is simple: put your best header on their most dangerous man, then work down. It sounds like common sense. On the numbers below, it concedes nearly a fifth more than the best plan.

## The concept

Pairing each of one group with exactly one of another, at the lowest total cost, is the **assignment problem**: workers to jobs, taxis to passengers, markers to attackers. With five markers there are 5 × 4 × 3 × 2 × 1 = **120** possible plans, few enough to check every one. With eleven, in man-to-man marking across the pitch, there are 39,916,800. With twenty there are about 2.4 billion billion.

The classic way to solve it without trying every plan is the **Hungarian method**, published by Harold Kuhn in 1955 and named after the Hungarian mathematicians Dénes Kőnig and Jenő Egerváry, whose earlier work it built on. In 2006 it turned out that Carl Gustav Jacobi had already solved the problem in the 19th century, in a paper published in Latin after his death.

## A football example

Here's how many goals we'd concede per 1,000 corners for each pairing (made-up numbers), if that marker picks up that attacker:

<figure class="rank-chart">
<div role="img" aria-label="A five by five grid of goals per 1,000 corners. The best plan, highlighted: big centre-back on their centre-back 6, second centre-back on the target man 8, holding midfielder on the late arriver 5, full-back on the striker 6, winger on the near-post runner 7, total 32. The obvious rule, outlined: big centre-back on the target man 6, second centre-back on the striker 8, holding midfielder on their centre-back 12, full-back on the runner 5, winger on the late arriver 7, total 38.">

</div>
<figcaption>Goals conceded per 1,000 corners for each marker (rows) on each attacker (columns), made up. Lower is better. Green is the best plan, 32. The gold outlines are the "best marker on their most dangerous man" plan, 38.</figcaption>
</figure>

A plan picks one number from each row and each column, and the goals conceded add up:

$$\text{conceded} = \sum_{\text{pairs}} \text{goals per pairing}$$

<div class="plain" markdown="1">
In plain football

- Each pairing has its own cost: how often that attacker scores if that marker has him.
- A plan uses each marker once and covers each attacker once, so it's one number from every row and every column of the grid.
- The best plan is the one whose five numbers add up to the least.
</div>

## Why the obvious rule loses

Their **target man** is the biggest threat, so the obvious rule gives him the marker who handles him best: our **big centre-back** (6). Next comes **their centre-back**. Our second centre-back is poor against him (13), so he goes to the holding midfielder (12). Working down the list gives a plan worth **38** goals per 1,000 corners.

The best plan, found by checking all 120, swaps our two centre-backs: the **big centre-back takes their centre-back** (6) and the **second centre-back takes the target man** (8). The target man gets a slightly worse marker, 8 instead of 6, but their centre-back gets a much better one, 6 instead of 12. The rest falls into place for a total of **32**.

The obvious rule looks at one attacker at a time. The best plan looks at all of them together. Our big centre-back is good against both of their big men, our second centre-back against only one, so the big centre-back should take the one the other can't handle.

The best plan also has the smallest **worst** pairing any plan can manage (8, against the obvious rule's 12). Here, "concede the fewest in total" and "avoid the worst mismatch" lead to the same plan. They don't have to: as in [Part 1](/learn/what-are-we-optimising), a different objective can give a different answer.

## The Hungarian method by hand

Once the two centre-backs are paired off, here are the other three markers against the other three attackers:

| Marker | Runner, late, striker |
|---|---|
| Holding midfielder | 7, 5, 7 |
| Full-back | 5, 8, 6 |
| Winger | 7, 7, 9 |

The Hungarian method's first two steps:

1. **Take each row's smallest number off that row.** Each marker's best option becomes 0, and the rest show how much worse they are.
2. **Then take each column's smallest off that column,** so each attacker's best marker also shows a 0.

That leaves

| Marker | Runner, late, striker |
|---|---|
| Holding midfielder | 2, 0, 1 |
| Full-back | 0, 3, 0 |
| Winger | 0, 0, 1 |

Now look for a **0 in every row and every column**: holding midfielder on the late arriver, full-back on the striker, winger on the runner. That plan costs nothing extra over the minimums taken off, so it's the best: 5 + 6 + 7 = **18**, the same three pairings the full search found. When the zeros don't line up like this, the method has a further step that shifts the numbers until they do. James Munkres showed in 1957 that it always gets there in a number of steps that grows only as a power of the problem's size, not explosively like the number of plans. `scipy`'s `linear_sum_assignment`, used in the snippet for the full grid, uses a later and faster method for the same problem, a version of the Jonker–Volgenant algorithm.

## Over a season

Real corners give the made-up numbers a scale. In the 2025/26 Scottish Premiership, a team faced on average **190 corners** over the season. At those numbers, the best plan concedes **6.1** goals from corners and the obvious rule **7.2**: about one goal a season, or **0.7 points** at the 0.60 points a goal conceded is worth (from [how many points is a goal worth?](/research/points-per-goal)). That's small for one plan, but it comes from changing nothing except who stands next to whom. For how much corners matter overall, see the [corners myth](/myth-or-maths/corners-win-matches).

## Why it matters

Pairing problems are everywhere in a club: markers to attackers, scouts to regions, coaches to players, players to positions. The obvious way to solve them, best first and work down, is called a greedy method. It's quick and often close, but it can lock in an early choice that costs more later, as it did with the two centre-backs. The assignment problem looks at every pairing together. The Hungarian method shows it can be solved fast and by hand for small cases, and with software for big ones. [Part 8](/learn/routes-through-the-press) moves from pairings to routes: the best way through a network, such as passing lanes through a press.

## Limitations

- **The threat numbers are made up.** Real values would come from tracking data on aerial duels and finishing, which our data doesn't have.
- **Corners aren't only man-to-man.** Many teams mark zones or mix zones with men, and attackers move to escape their marker. The assignment problem fits man-marking best.
- **The corners count is real, the rest isn't.** 190 is the 2025/26 average; the goals per corner come from the made-up table.
- **Pairings interact.** A marker's job can depend on where his teammates are, which a table of separate pairings can't capture.

## Try it yourself

Make our second centre-back better against their centre-back: change his 13 to 7. Before running the code, guess: does the obvious rule now find the best plan? Then add a sixth attacker and a sixth marker, and see how the number of plans grows.

## Reproduce the analysis

This needs `scipy` (`pip install scipy`) and, for the season figures, the 2025/26 Scottish Premiership file (SC0) from [football-data.co.uk](https://www.football-data.co.uk/scotlandm.php), saved as `SC0_2526.csv`; it isn't rehosted on this site. Then:

```python
import csv
from itertools import permutations
from math import factorial

from scipy.optimize import linear_sum_assignment

# Made up: goals conceded per 1,000 corners if each of our markers (rows) picks up each of their attackers (columns).
markers = ["Big centre-back", "Second centre-back", "Holding midfielder", "Full-back", "Winger"]
attackers = ["Target man", "Their centre-back", "Near-post runner", "Late arriver", "Striker"]
threat = [[6, 6, 10, 8, 8],
          [8, 13, 9, 7, 8],
          [15, 12, 7, 5, 7],
          [18, 13, 5, 8, 6],
          [21, 15, 7, 7, 9]]
n = len(markers)
total = lambda plan: sum(threat[i][plan[i]] for i in range(n))   # plan[i] = the attacker marker i picks up


def show(label, plan):
    pairs = "; ".join(f"{markers[i]} on {attackers[plan[i]]}" for i in range(n))
    print(f"{label}: {total(plan)} per 1,000 corners\n  {pairs}")


# The obvious rule: take their most dangerous attacker first, give him our best marker for him, and so on down.
danger = sorted(range(n), key=lambda j: -sum(row[j] for row in threat))
free, greedy = set(range(n)), [None] * n
for j in danger:
    i = min(free, key=lambda i: threat[i][j])
    greedy[i] = j
    free.remove(i)
show("Best marker on their most dangerous man first", greedy)

plans = list(permutations(range(n)))
best = min(plans, key=total)
show(f"Best of all {len(plans)} plans", best)
print(f"  (the worst plan concedes {max(map(total, plans))})")
rows, cols = linear_sum_assignment(threat)
print(f"scipy's linear_sum_assignment agrees: {list(cols) == list(best)}")
worst_pair = lambda plan: max(threat[i][plan[i]] for i in range(n))
print(f"Worst single pairing: {worst_pair(greedy)} with the obvious rule, {worst_pair(best)} with the best plan; "
      f"the smallest any plan can manage is {min(map(worst_pair, plans))}")

# The Hungarian method by hand, on the last three markers and attackers.
sub = [row[2:] for row in threat[2:]]
reduced = [[v - min(row) for v in row] for row in sub]                  # take each row's smallest off the row
reduced = [[row[j] - min(r[j] for r in reduced) for j in range(3)] for row in reduced]   # then each column's
print("\nHungarian method on the holding midfielder, full-back and winger:")
for name, row in zip(markers[2:], reduced):
    print(f"  {name:<19}" + "".join(f"{v:4}" for v in row))
zeros = [p for p in permutations(range(3)) if all(reduced[i][p[i]] == 0 for i in range(3))]
print("  a zero in every row and column: " + "; ".join(f"{markers[2 + i]} on {attackers[2 + zeros[0][i]]}" for i in range(3)))

print("\nHow many plans to try:")
for k in (5, 11, 20):
    print(f"  {k} markers: {factorial(k):,}")

# Real: corners per team in the 2025/26 Scottish Premiership (needs SC0_2526.csv from football-data.co.uk).
with open("SC0_2526.csv", encoding="latin-1") as f:
    games = [r for r in csv.DictReader(f) if r.get("HC")]
corners = sum(int(r["HC"]) + int(r["AC"]) for r in games) / (2 * len(games)) * 38
print(f"\nCorners faced per team-season, 2025/26: {corners:.0f}")
print(f"Goals conceded from them: {corners * total(best) / 1000:.1f} with the best plan, "
      f"{corners * total(greedy) / 1000:.1f} with the obvious rule; "
      f"about {corners * (total(greedy) - total(best)) / 1000 * 0.60:.1f} points a season at 0.60 a goal")
```

It prints:

```text
Best marker on their most dangerous man first: 38 per 1,000 corners
  Big centre-back on Target man; Second centre-back on Striker; Holding midfielder on Their centre-back; Full-back on Near-post runner; Winger on Late arriver
Best of all 120 plans: 32 per 1,000 corners
  Big centre-back on Their centre-back; Second centre-back on Target man; Holding midfielder on Late arriver; Full-back on Striker; Winger on Near-post runner
  (the worst plan concedes 59)
scipy's linear_sum_assignment agrees: True
Worst single pairing: 12 with the obvious rule, 8 with the best plan; the smallest any plan can manage is 8

Hungarian method on the holding midfielder, full-back and winger:
  Holding midfielder    2   0   1
  Full-back             0   3   0
  Winger                0   0   1
  a zero in every row and column: Holding midfielder on Late arriver; Full-back on Striker; Winger on Near-post runner

How many plans to try:
  5 markers: 120
  11 markers: 39,916,800
  20 markers: 2,432,902,008,176,640,000

Corners faced per team-season, 2025/26: 190
Goals conceded from them: 6.1 with the best plan, 7.2 with the obvious rule; about 0.7 points a season at 0.60 a goal
```

The obvious rule ranks their attackers by their average threat across our markers. Ranking them another way changes its plan, but not the lesson: any rule that settles one pairing at a time can miss the best set.

## Further reading

- [Assignment problem](https://en.wikipedia.org/wiki/Assignment_problem), Wikipedia. The problem and the main ways to solve it.
- [Hungarian algorithm](https://en.wikipedia.org/wiki/Hungarian_algorithm), Wikipedia. Kuhn's method step by step, its history and Jacobi's earlier solution.
- [scipy.optimize.linear_sum_assignment](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linear_sum_assignment.html), the SciPy documentation for the solver used here.
