Problem ADAPWN
We are given a NxN
chess board and some number of pawns on the board. Every pawn on (i, j)
threatens cells (i-1, j-1)
and (i-1, j+1)
and if there is a pawn on either of these cells, it can capture one of them. The goal is to declare a sequence of captures (note that ordering matters!) such that after captures are performed, no pawn is under a threat by another pawn anymore.
For example, given this starting position:
Here is one optimal sequence of captures:
Which results in a no-threat situation:
Example input:
1
8
....O...
.....O..
....O.O.
..O..O..
.O.O.O..
..O.O...
.....O..
....O...
Example output:
6
7 6 L
4 6 L
2 6 L
5 2 R
5 4 L
6 5 R
Note that captures 7 6 L
and 6 5 R
cannot be reordered!
Ideas
First observation is to think of a capture as removal of a pawn. when pawn p1
captures, pawn p2
, it is equivalent of just removing pawn p1
. However, if a pawn is not attacking another pawn, it cannot be removed (this made things a bit tricky at the end).
I tried a lot of heuristics, such as eliminating in certain order or going with the highest degree nodes first. But none worked and an example like this made me realize why picking the highest degree pawn is not the best choice:
After trying a few ideas on paper and coding brute-force to try them out, I was almost certain there has to be some sort of optimization to solve this problem.
Game Theory
There is a pretty elegant solution using grundy numbers (aka Nimber). You can read about that solution from the official editorial page here.
Graph Theory
Imagine a graph where each pawn is a vertex and any pawn threatening another pawn is an edge between those two. The objective of this problem is to not have any threats left. Which means, for each edge e = (p1, p2)
either p1
or p2
(or both) should be eliminated in such a way that minimum pawns are eliminated in total. This is basically minimum vertex cover, where we need to pick minimum number of vertices such that every edge is adjacent to least one picked vertex. While the vertex cover problem in general is NP-Complete
, it can be solved fairly efficiently on a bipartite graph using Kőnig’s Theorem. This page has a pretty good visual explanation of the theorem. Basically, the problem becomes finding the maximum matching on a bipartite graph.
Bi-partite Graph
A graph is bi-partite if it doesn’t have any cycles or all cycles are of even length. You can prove that our graph in this problem is bi-partite by showing it cannot have an odd cycle. Also, if you are into graph theory, you probably know that every planar graph whose faces all have even length is bipartite. However, in this case it is even simpler. Just assign pawns on even rows to one group and pawns on odd rows to another group.
Handling Final Pawns
Notice that for even a simple case where there are two pawns and p1
is threatening p2
, you can either eliminate p1
or p2
to eliminate the threat. However, since the problem asks for a “pawn capture”, p2
can’t capture any pawn and we can only eliminate p1
. So, a simple bipartite vertex cover problem wouldn’t be enough since it may pick p2
as the pawn to be removed. In order to cover these cases, imagine a pawn c
that doesn’t threaten any other pawn but is threatened by pawns p1
, p2
, p3
. Since c
cannot be eliminated, in order to remove the threat, we have to eliminate all p1
, p2
and p3
. So, we can remove these pawns and all the edges connected to them, to find the vertex cover on the remaining graph. At the end we can add the removed pawns to the final solution.
Solution Overview
- read input and store the grid
- create a parent/child graph where
p1
is parent ofp2
ifp1
attacksp2
- mark any node that has a childless child (and add them to the
must_takes
list) - mark any node that doesn’t have a child
- remove all marked nodes
- use dfs to create a graph of the remaining pawns
- create the bipartite graph
- run bipartite vertex cover and get
cover
as minimum pawns chosen - print the list of
cover
+must_takes
as the final answer