Red-Green-Code

Deliberate practice techniques for software developers

  • Home
  • About
  • Contact
  • Project 462
  • CP FAQ
  • Newsletter

When Not to Simulate a Game (UVa 11553)

By Duncan Smith Feb 10 0

Grid

Some programming puzzles can be solved by simulating a game or process. For example:

  • UVa 978 is based on simulating a conflict between warring lemmings.
  • For UVa 732, you have to simulate something more abstract: a stack that pushes and pops characters.

UVa 11553 may seem like a classic simulation problem: a board game between two players. But it soon becomes clear that this problem can’t be solved by simulating the players’ moves.

The Problem

Two players, Alice and Bob, play a game on a $n \times n$ grid containing random integers. On each turn, Alice crosses out a row and Bob crosses out a column. The integer at the intersection of the row and column is added to a total score. The game ends when every row and every column have been crossed out. Alice’s goal is to maximize the total score. Bob’s goal is to minimize it. Given a particular game board, what is the best score Alice can achieve, assuming Alice and Bob both play perfectly.

Complete Search

This problem is in the Complete Search section of uHunt Chapter 3, so let’s see what it would take to search all possible move options for both players.

Alice gets the first move, and can choose any of $n$ rows. Bob can then respond by choosing any of $n$ columns. On the next turn, one row is crossed out, so Alice only has $n-1$ choices. Similarly, Bob must choose one of the $n-1$ remaining columns. The game proceeds until the last turn, when each player only has one choice. Therefore, the total number of possible games is $n \cdot n \cdot (n-1) \cdot (n-1) \cdot … \cdot 1 \cdot 1$, or ($n! \cdot n!$).

The problem statement tells us that $n$ can be up to 8, so to check all possibilities on the largest board, we would have to search $(8!)^2 = 40320^2 \approx 1.6 \times 10^9$ games. Calculating the total score for each game requires keeping track of the sum of $n$ integers, which you might do using another inner loop of size $n$, for a total of $(8!)^2 \cdot 8 \approx 1.3 \times 10^{10}$ operations to do the main simulation. Opinions vary about how many operations per second one should assume an online judge can carry out. But $10^{10}$ operations in one second (the time limit for this problem) is beyond even the most optimistic estimates.

A Greedy Solution?

Since we can’t simulate the whole game for the largest board, is there a greedy solution we can use to cut down on the number of turns that need to be searched? Greedy problems have their own section in uHunt Chapter 3, but let’s pretend we encountered this problem in the wild, and didn’t get that hint.

Since Alice is trying to maximize her score, and she gets to go first, maybe there’s an order she can pick rows so Bob is forced to get higher numbers than he wants on each turn.

Consider this $3 \times 3$ grid:

 1 -2 -3
-3  4 -5
-2  1  3

That -5 looks bad for Alice, so what if she starts the game by picking the first row? She might try that as a way of enticing Bob into greedily picking the third column and getting (for him) the best number on that row, -3. That takes the third column out of play, which means the -5 can never be picked, pushing the final score in Alice’s favor. The total game score might come out to $(-3) + (-3) + 1 = -5$.

But this sequence of moves violates the requirement for both players to play perfectly. There’s nothing preventing Bob from selecting other columns until Alice selects row 2 and he can get that nice -5. In fact, that would be a better strategy for him.

A Solution Without Simulation

Once you start thinking about how to come up with a greedy solution, you may discover the surprise fact about this problem: Alice’s moves are completely irrelevant. She has no influence over the final score, regardless of which rows she picks. Since Bob plays last on each turn, he gets to pick the optimal number in each row. The only restriction that applies to him is that he can’t pick two numbers in the same column.

So rather than a game between two players, we can reduce the problem to:

Given a grid of random integers, pick one number from each row such that the sum of your selected numbers is as small as possible, and no two of your numbers are from the same column.

Bob’s goal is to minimize the total score. If Bob has access to a computer, he can accomplish this as follows using the revised problem statement:

  • Consider all $n!$ permutations of number selections: one of the $n$ numbers from the first row, one of the $n-1$ remaining numbers in a different column from the second row, and so on.
  • Of those permutations, pick the one with the lowest sum, and remember the optimal column for each row.
  • When Alice selects a row, pick the optimal column for that row. The order in which Alice selects the rows does not affect the outcome, since Bob knows that Alice eventually has to pick every row.

Using this process, Bob can always get the lowest possible score given the constraints of the problem. And since there are $8! = 40320$ permutations at most, an online judge will execute this solution in well under one second, even using Java.

Pseudocode

Here’s how the solution works for each test case:

read the grid input
create an array of ints called colOrder, initialized
    to the starting order 0, 1, ..., n-1
for each permutation of colOrder
    for i from 0 to n-1
        retrieve the grid value at
            row i and the column number at
            position i in colOrder
        increment the total by that value
    if this total is smaller than any total found
        so far, update the minimum total
print the minimum total found

This solution is based on generating all permutations of the column numbers $0$ through $n-1$. Each of these permutations represents one of the $n!$ possible mapping of row numbers to column numbers. The one that produces the lowest sum is the one Bob should use. For this problem, we only need to save the total, not the permutation.

If you’re using C++, you can generate permutations using the next_permutation STL function. If you’re using a different language, take a look at Project Nayuki’s next lexicographical permutation algorithm.

Thinking Through This Problem

If you didn’t immediately notice the trick for this problem, try thinking through solutions in order of decreasing performance. The brute-force solution (trying all possibilities) will give you a basic idea of how the game works, but doesn’t provide much insight into what the problem is really asking. Thinking about the greedy approach should lead you to the realization that Bob has complete control of this game, and doesn’t have to make decisions based on Alice’s moves. From there, you can design a fast solution that only considers Bob’s moves.

(Image credit: Robson#)

Categories: Competitive Programming

Prev
Next

Stay in the Know

I'm trying out the latest learning techniques on software development concepts, and writing about what works best. Sound interesting? Subscribe to my free newsletter to keep up to date. Learn More
Unsubscribing is easy, and I'll keep your email address private.

Getting Started

Are you new here? Check out my review posts for a tour of the archives:

  • 2023 in Review: 50 LeetCode Tips
  • 2022 in Review: Content Bots
  • 2021 in Review: Thoughts on Solving Programming Puzzles
  • Lessons from the 2020 LeetCode Monthly Challenges
  • 2019 in Review
  • Competitive Programming Frequently Asked Questions: 2018 In Review
  • What I Learned Working On Time Tortoise in 2017
  • 2016 in Review
  • 2015 in Review
  • 2015 Summer Review

Archives

Recent Posts

  • Do Coding Bots Mean the End of Coding Interviews? December 31, 2024
  • Another Project for 2024 May 8, 2024
  • Dynamic Programming Wrap-Up May 1, 2024
  • LeetCode 91: Decode Ways April 24, 2024
  • LeetCode 70: Climbing Stairs April 17, 2024
  • LeetCode 221: Maximal Square April 10, 2024
  • Using Dynamic Programming for Maximum Product Subarray April 3, 2024
  • LeetCode 62: Unique Paths March 27, 2024
  • LeetCode 416: Partition Equal Subset Sum March 20, 2024
  • LeetCode 1143: Longest Common Subsequence March 13, 2024
Red-Green-Code
  • Home
  • About
  • Contact
  • Project 462
  • CP FAQ
  • Newsletter
Copyright © 2025 Duncan Smith