The Constraint Programming Solver

Example 6.2 Alphabet Blocks Problem

(View the complete code for this example.)

This example illustrates the use of the global cardinality constraint (GCC). The alphabet blocks problem consists of finding an arrangement of letters on four alphabet blocks. Each alphabet block has a single letter on each of its six sides. Collectively, the four blocks contain every letter of the alphabet except Q and Z. By arranging the blocks in various ways, the following words should be spelled out: BAKE, ONYX, ECHO, OVAL, GIRD, SMUG, JUMP, TORN, LUCK, VINY, LUSH, and WRAP.

You can formulate this problem as a CSP by representing each of the 24 letters as an integer variable. The domain of each variable is the set StartSet 1 comma 2 comma 3 comma 4 EndSet, which represents block1 through block4. The assignment A = 1 indicates that the letter A is on a side of block1. Because each block has six sides, each value v in StartSet 1 comma 2 comma 3 comma 4 EndSet must be assigned to exactly six variables so that each side of a block has a letter on it. This restriction can be formulated as a global cardinality constraint over all 24 variables, with common lower and upper bounds set equal to 6.

Moreover, in order to spell all the words listed previously, the four letters in each of the 12 words must be on different blocks. Another GCC statement that specifies 12 global cardinality constraints enforces these conditions. You can also formulate these restrictions by using 12 all-different constraints. Finally, four FIX statements break the symmetries that blocks are interchangeable. These constraints preset the blocks that contain the letters B, A, K, and E as block1, block2, block3, and block4, respectively.

The complete representation of the problem is as follows:

proc optmodel;
   /* Each letter except Q and Z is represented with a variable. */
   /* The domain of each variable is the set of 4 blocks,        */
   /*   or {1, 2, 3, 4} for short.                               */
   set LETTERS = / A B C D E F G H I J K L M N O P R S T U V W X Y /;
   var Block {LETTERS} integer >= 1 <= 4;
   set BLOCKS = 1..4;

   /* There are exactly 6 letters on each alphabet block */
   con SixLettersPerBlock:
      gcc(Block, setof {b in BLOCKS} <b,6,6>);

   /* The letters in each word must be on different blocks. */
   set WORDS = / BAKE ONYX ECHO OVAL GIRD SMUG JUMP TORN LUCK VINY LUSH WRAP /;
   con CanSpell {w in WORDS}:
      gcc({k in 1..length(w)} Block[char(w,k)], setof {b in BLOCKS} <b,0,1>);

   /* Note 2: These restrictions can also be enforced by ALLDIFF constraints:
   con CanSpellv2 {w in WORDS}:
      alldiff({k in 1..length(w)} Block[char(w,k)]);
   */

   /* Breaking the symmetry that blocks can be interchanged by setting
      the block that contains the letter B as block1, the block that
      contains the letter A as block2, etc. */
   for {k in 1..length('BAKE')} fix Block[char('BAKE',k)] = k;

   solve;
   print Block;
quit;

The solution to this problem is shown in Output 6.2.1.

Output 6.2.1: Solution to Alphabet Blocks Problem

The OPTMODEL Procedure

Problem Summary
Objective SenseMinimization
Objective Function(0)
Objective TypeConstant
  
Number of Variables24
Bounded Above0
Bounded Below0
Bounded Below and Above20
Free0
Fixed4
Binary1
Integer23
  
Number of Constraints13
Linear LE (<=)0
Linear EQ (=)0
Linear GE (>=)0
Linear Range0
Alldiff0
Cumulative0
Element0
GCC13
Lexico (<=)0
Lexico (<)0
Pack0
Reify0
  
Constraint Coefficients0

Solution Summary
SolverCLP
Variable SelectionMINR
Objective Function(0)
Solution StatusSolution Limit Reached
Objective Value0
  
Solutions Found1
Presolve Time0.00
Solution Time0.00

[1]Block
A2
B1
C2
D2
E4
F1
G4
H3
I1
J2
K3
L4
M3
N2
O1
P4
R3
S2
T4
U1
V3
W1
X3
Y4


Last updated: April 14, 2021