Tim Varley Logo
Tim Varley Principal AI Engineer and Tech Leader
Problem #90 medium

Cube digit pairs

Each of the six faces on a cube has a different digit (00 to 99) written on it; the same is done to a second cube. By placing the two cubes side-by-side in different positions we can form a variety of 22-digit numbers.

For example, the square number 6464 could be formed:

Cube digit pairs example

In fact, by carefully choosing the digits on both cubes it is possible to display all of the square numbers below one-hundred: 0101, 0404, 0909, 1616, 2525, 3636, 4949, 6464, and 8181.

For example, one way this can be achieved is by placing {0,5,6,7,8,9}\{0, 5, 6, 7, 8, 9\} on one cube and {1,2,3,4,8,9}\{1, 2, 3, 4, 8, 9\} on the other cube.

However, for this problem we shall allow the 66 or 99 to be turned upside-down so that an arrangement like {0,5,6,7,8,9}\{0, 5, 6, 7, 8, 9\} and {1,2,3,4,6,7}\{1, 2, 3, 4, 6, 7\} allows for all nine square numbers to be displayed; otherwise it would be impossible to obtain 0909.

In determining a distinct arrangement we are interested in the digits on each cube, not the order.

  • {1,2,3,4,5,6}\{1, 2, 3, 4, 5, 6\} is equivalent to {3,6,4,1,2,5}\{3, 6, 4, 1, 2, 5\}
  • {1,2,3,4,5,6}\{1, 2, 3, 4, 5, 6\} is distinct from {1,2,3,4,5,9}\{1, 2, 3, 4, 5, 9\}

But because we are allowing 66 and 99 to be reversed, the two distinct sets in the last example both represent the extended set {1,2,3,4,5,6,9}\{1, 2, 3, 4, 5, 6, 9\} for the purpose of forming 22-digit numbers.

How many distinct arrangements of the two cubes allow for all of the square numbers to be displayed?

View on Project Euler

Implementations

cpp
#include <iostream>
#include <vector>
bool has_digit(int mask, int d) {
if (mask & (1 << d)) return true;
if (d == 6 && (mask & (1 << 9))) return true;
if (d == 9 && (mask & (1 << 6))) return true;
return false;
}
bool can_form_square(int mask_a, int mask_b, int sq) {
int d1 = sq / 10, d2 = sq % 10;
bool a1 = has_digit(mask_a, d1), a2 = has_digit(mask_a, d2);
bool b1 = has_digit(mask_b, d1), b2 = has_digit(mask_b, d2);
return (a1 && b2) || (a2 && b1);
}
long long cube_digit_pairs() {
std::vector<int> squares = {1, 4, 9, 16, 25, 36, 49, 64, 81};
std::vector<int> cubes;
for (int i = 0; i < (1 << 10); ++i) {
if (__builtin_popcount(i) == 6) {
cubes.push_back(i);
}
}
long long count = 0;
int n = cubes.size();
for (int i = 0; i < n; ++i) {
int ma = cubes[i];
for (int j = i + 1; j < n; ++j) {
int mb = cubes[j];
bool ok = true;
for (int sq : squares) {
if (!can_form_square(ma, mb, sq)) {
ok = false;
break;
}
}
if (ok) ++count;
}
}
return count;
}

Solution Notes

Mathematical Background

This problem involves finding pairs of cubes with digits 0-9 that can form all two-digit square numbers (01 through 81) by arranging the cubes side by side. The cubes have 6 faces each, and digits 6 and 9 are considered interchangeable since they can be rotated.

Algorithm Analysis

The solution generates all possible combinations of 6 digits from 10 (C(10,6) = 210) for each cube using bitmasks or combinations. It then checks all unordered pairs to see if they can form each required square by ensuring the digits are available on either cube in the correct positions.

Performance

All implementations run efficiently in under 100ms, with the combinatorial approach being fast due to the small number of combinations.

Key Insights

  • Bitmasks provide efficient digit checking and storage.
  • Handling 6/9 interchangeability is crucial for covering squares like 09 and 90.
  • The problem requires checking both possible arrangements for each square digit pair.

Educational Value

Demonstrates combinatorial enumeration and bit manipulation techniques, with a practical application to puzzle-solving and constraint satisfaction.

Problem #90 is taken from Project Euler and licensed under CC BY-NC-SA 4.0.