examples/pbn.mod
author Alpar Juttner <alpar@cs.elte.hu>
Mon, 06 Dec 2010 13:09:21 +0100
changeset 1 c445c931472f
permissions -rw-r--r--
Import glpk-4.45

- Generated files and doc/notes are removed
alpar@1
     1
/* PBN, Paint-By-Numbers Puzzle */
alpar@1
     2
alpar@1
     3
/* Written in GNU MathProg by Andrew Makhorin <mao@gnu.org> */
alpar@1
     4
alpar@1
     5
/* A paint-by-number puzzle consists of an m*n grid of pixels (the
alpar@1
     6
   canvas) together with m+n cluster-size sequences, one for each row
alpar@1
     7
   and column. The goal is to paint the canvas with a picture that
alpar@1
     8
   satisfies the following constraints:
alpar@1
     9
alpar@1
    10
   1. Each pixel must be blank or white.
alpar@1
    11
alpar@1
    12
   2. If a row or column has cluster-size sequence s1, s2, ..., sk,
alpar@1
    13
      then it must contain k clusters of black pixels - the first with
alpar@1
    14
      s1 black pixels, the second with s2 black pixels, and so on.
alpar@1
    15
alpar@1
    16
   It should be noted that "first" means "leftmost" for rows and
alpar@1
    17
   "topmost" for columns, and that rows and columns need not begin or
alpar@1
    18
   end with black pixels.
alpar@1
    19
alpar@1
    20
   Example:
alpar@1
    21
                  1   1
alpar@1
    22
                  1   1
alpar@1
    23
              2 1 1 1 1 1 2 3
alpar@1
    24
              3 2 1 2 1 2 3 4 8 9
alpar@1
    25
alpar@1
    26
        3 6   # # # . # # # # # #
alpar@1
    27
        1 4   # . . . . . # # # #
alpar@1
    28
      1 1 3   . . # . # . . # # #
alpar@1
    29
          2   . . . . . . . . # #
alpar@1
    30
        3 3   . . # # # . . # # #
alpar@1
    31
        1 4   # . . . . . # # # #
alpar@1
    32
        2 5   # # . . . # # # # #
alpar@1
    33
        2 5   # # . . . # # # # #
alpar@1
    34
        1 1   . . . # . . . . . #
alpar@1
    35
          3   . . # # # . . . . .
alpar@1
    36
alpar@1
    37
   (In Russia this sort of puzzles is known as "Japanese crossword".)
alpar@1
    38
alpar@1
    39
   References:
alpar@1
    40
   Robert A. Bosch, "Painting by Numbers", 2000.
alpar@1
    41
   <http://www.oberlin.edu/~math/faculty/bosch/pbn-page.html> */
alpar@1
    42
alpar@1
    43
param m, integer, >= 1;
alpar@1
    44
/* the number of rows */
alpar@1
    45
alpar@1
    46
param n, integer, >= 1;
alpar@1
    47
/* the number of columns */
alpar@1
    48
alpar@1
    49
param row{i in 1..m, 1..n div 2}, integer, >= 0, default 0;
alpar@1
    50
/* the cluster-size sequence for row i (raw data) */
alpar@1
    51
alpar@1
    52
param col{j in 1..n, 1..m div 2}, integer, >= 0, default 0;
alpar@1
    53
/* the cluster-size sequence for column j (raw data) */
alpar@1
    54
alpar@1
    55
param kr{i in 1..m} := sum{t in 1..n div 2: row[i,t] > 0} 1;
alpar@1
    56
/* the number of clusters in row i */
alpar@1
    57
alpar@1
    58
param kc{j in 1..n} := sum{t in 1..m div 2: col[j,t] > 0} 1;
alpar@1
    59
/* the number of clusters in column j */
alpar@1
    60
alpar@1
    61
param sr{i in 1..m, t in 1..kr[i]} := row[i,t], integer, >= 1;
alpar@1
    62
/* the cluster-size sequence for row i */
alpar@1
    63
alpar@1
    64
param sc{j in 1..n, t in 1..kc[j]} := col[j,t], integer, >= 1;
alpar@1
    65
/* the cluster-size sequence for column j */
alpar@1
    66
alpar@1
    67
check{i in 1..m}: sum{t in 1..kr[i]} sr[i,t] <= n - (kr[i] - 1);
alpar@1
    68
/* check that the sum of the cluster sizes in each row is valid */
alpar@1
    69
alpar@1
    70
check{j in 1..n}: sum{t in 1..kc[j]} sc[j,t] <= m - (kc[j] - 1);
alpar@1
    71
/* check that the sum of the cluster sizes in each column is valid */
alpar@1
    72
alpar@1
    73
check: sum{i in 1..m, t in 1..kr[i]} sr[i,t] =
alpar@1
    74
       sum{j in 1..n, t in 1..kc[j]} sc[j,t];
alpar@1
    75
/* check that the sum of the cluster sizes in all rows is equal to the
alpar@1
    76
   sum of the cluster sizes in all columns */
alpar@1
    77
alpar@1
    78
param er{i in 1..m, t in 1..kr[i]} :=
alpar@1
    79
   if t = 1 then 1 else er[i,t-1] + sr[i,t-1] + 1;
alpar@1
    80
/* the smallest value of j such that row i's t-th cluster can be
alpar@1
    81
   placed in row i with its leftmost pixel occupying pixel j */
alpar@1
    82
alpar@1
    83
param lr{i in 1..m, t in 1..kr[i]} :=
alpar@1
    84
   if t = kr[i] then n + 1 - sr[i,t] else lr[i,t+1] - sr[i,t] - 1;
alpar@1
    85
/* the largest value of j such that row i's t-th cluster can be
alpar@1
    86
   placed in row i with its leftmost pixel occupying pixel j */
alpar@1
    87
alpar@1
    88
param ec{j in 1..n, t in 1..kc[j]} :=
alpar@1
    89
   if t = 1 then 1 else ec[j,t-1] + sc[j,t-1] + 1;
alpar@1
    90
/* the smallest value of i such that column j's t-th cluster can be
alpar@1
    91
   placed in column j with its topmost pixel occupying pixel i */
alpar@1
    92
alpar@1
    93
param lc{j in 1..n, t in 1..kc[j]} :=
alpar@1
    94
   if t = kc[j] then m + 1 - sc[j,t] else lc[j,t+1] - sc[j,t] - 1;
alpar@1
    95
/* the largest value of i such that column j's t-th cluster can be
alpar@1
    96
   placed in column j with its topmost pixel occupying pixel i */
alpar@1
    97
alpar@1
    98
var z{i in 1..m, j in 1..n}, binary;
alpar@1
    99
/* z[i,j] = 1, if row i's j-th pixel is painted black
alpar@1
   100
   z[i,j] = 0, if row i's j-th pixel is painted white */
alpar@1
   101
alpar@1
   102
var y{i in 1..m, t in 1..kr[i], j in er[i,t]..lr[i,t]}, binary;
alpar@1
   103
/* y[i,t,j] = 1, if row i's t-th cluster is placed in row i with its
alpar@1
   104
                 leftmost pixel occupying pixel j
alpar@1
   105
   y[i,t,j] = 0, if not */
alpar@1
   106
alpar@1
   107
var x{j in 1..n, t in 1..kc[j], i in ec[j,t]..lc[j,t]}, binary;
alpar@1
   108
/* x[j,t,i] = 1, if column j's t-th cluster is placed in column j with
alpar@1
   109
                 its topmost pixel occupying pixel i
alpar@1
   110
   x[j,t,i] = 0, if not */
alpar@1
   111
alpar@1
   112
s.t. fa{i in 1..m, t in 1..kr[i]}:
alpar@1
   113
     sum{j in er[i,t]..lr[i,t]} y[i,t,j] = 1;
alpar@1
   114
/* row i's t-th cluster must appear in row i exactly once */
alpar@1
   115
alpar@1
   116
s.t. fb{i in 1..m, t in 1..kr[i]-1, j in er[i,t]..lr[i,t]}:
alpar@1
   117
     y[i,t,j] <= sum{jp in j+sr[i,t]+1..lr[i,t+1]} y[i,t+1,jp];
alpar@1
   118
/* row i's (t+1)-th cluster must be placed to the right of its t-th
alpar@1
   119
   cluster */
alpar@1
   120
alpar@1
   121
s.t. fc{j in 1..n, t in 1..kc[j]}:
alpar@1
   122
     sum{i in ec[j,t]..lc[j,t]} x[j,t,i] = 1;
alpar@1
   123
/* column j's t-th cluster must appear in column j exactly once */
alpar@1
   124
alpar@1
   125
s.t. fd{j in 1..n, t in 1..kc[j]-1, i in ec[j,t]..lc[j,t]}:
alpar@1
   126
     x[j,t,i] <= sum{ip in i+sc[j,t]+1..lc[j,t+1]} x[j,t+1,ip];
alpar@1
   127
/* column j's (t+1)-th cluster must be placed below its t-th cluster */
alpar@1
   128
alpar@1
   129
s.t. fe{i in 1..m, j in 1..n}:
alpar@1
   130
     z[i,j] <= sum{t in 1..kr[i], jp in er[i,t]..lr[i,t]:
alpar@1
   131
                   j-sr[i,t]+1 <= jp and jp <= j} y[i,t,jp];
alpar@1
   132
/* the double coverage constraint stating that if row i's j-th pixel
alpar@1
   133
   is painted black, then at least one of row i's clusters must be
alpar@1
   134
   placed in such a way that it covers row i's j-th pixel */
alpar@1
   135
alpar@1
   136
s.t. ff{i in 1..m, j in 1..n}:
alpar@1
   137
     z[i,j] <= sum{t in 1..kc[j], ip in ec[j,t]..lc[j,t]:
alpar@1
   138
                   i-sc[j,t]+1 <= ip and ip <= i} x[j,t,ip];
alpar@1
   139
/* the double coverage constraint making sure that if row i's j-th
alpar@1
   140
   pixel is painted black, then at least one of column j's clusters
alpar@1
   141
   covers it */
alpar@1
   142
alpar@1
   143
s.t. fg{i in 1..m, j in 1..n, t in 1..kr[i], jp in er[i,t]..lr[i,t]:
alpar@1
   144
     j-sr[i,t]+1 <= jp and jp <= j}: z[i,j] >= y[i,t,jp];
alpar@1
   145
/* the constraint to prevent white pixels from being covered by the
alpar@1
   146
   row clusters */
alpar@1
   147
alpar@1
   148
s.t. fh{i in 1..m, j in 1..n, t in 1..kc[j], ip in ec[j,t]..lc[j,t]:
alpar@1
   149
     i-sc[j,t]+1 <= ip and ip <= i}: z[i,j] >= x[j,t,ip];
alpar@1
   150
/* the constraint to prevent white pixels from being covered by the
alpar@1
   151
   column clusters */
alpar@1
   152
alpar@1
   153
/* there is no need for an objective function here */
alpar@1
   154
alpar@1
   155
solve;
alpar@1
   156
alpar@1
   157
for {i in 1..m}
alpar@1
   158
{  printf{j in 1..n} " %s", if z[i,j] then "#" else ".";
alpar@1
   159
   printf "\n";
alpar@1
   160
}
alpar@1
   161
alpar@1
   162
data;
alpar@1
   163
alpar@1
   164
/* These data correspond to the example above. */
alpar@1
   165
alpar@1
   166
param m := 10;
alpar@1
   167
alpar@1
   168
param n := 10;
alpar@1
   169
alpar@1
   170
param row : 1 2 3 4 :=
alpar@1
   171
         1  3 6 . .
alpar@1
   172
         2  1 4 . .
alpar@1
   173
         3  1 1 3 .
alpar@1
   174
         4  2 . . .
alpar@1
   175
         5  3 3 . .
alpar@1
   176
         6  1 4 . .
alpar@1
   177
         7  2 5 . .
alpar@1
   178
         8  2 5 . .
alpar@1
   179
         9  1 1 . .
alpar@1
   180
        10  3 . . . ;
alpar@1
   181
alpar@1
   182
param col : 1 2 3 4 :=
alpar@1
   183
         1  2 3 . .
alpar@1
   184
         2  1 2 . .
alpar@1
   185
         3  1 1 1 1
alpar@1
   186
         4  1 2 . .
alpar@1
   187
         5  1 1 1 1
alpar@1
   188
         6  1 2 . .
alpar@1
   189
         7  2 3 . .
alpar@1
   190
         8  3 4 . .
alpar@1
   191
         9  8 . . .
alpar@1
   192
        10  9 . . . ;
alpar@1
   193
alpar@1
   194
end;