COIN-OR::LEMON - Graph Library

source: lemon-project-template-glpk/deps/glpk/examples/pbn/pbn.mod

subpack-glpk
Last change on this file was 9:33de93886c88, checked in by Alpar Juttner <alpar@…>, 13 years ago

Import GLPK 4.47

File size: 9.2 KB
Line 
1/* PBN, Paint-By-Numbers Puzzle */
2
3/* Written in GNU MathProg by Andrew Makhorin <mao@gnu.org> */
4
5/* NOTE: See also the document "Solving Paint-By-Numbers Puzzles with
6         GLPK", which is included in the GLPK distribution. */
7
8/* A paint-by-numbers puzzle consists of an m*n grid of pixels (the
9   canvas) together with m+n cluster-size sequences, one for each row
10   and column. The goal is to paint the canvas with a picture that
11   satisfies the following constraints:
12
13   1. Each pixel must be blank or white.
14
15   2. If a row or column has cluster-size sequence s1, s2, ..., sk,
16      then it must contain k clusters of black pixels - the first with
17      s1 black pixels, the second with s2 black pixels, and so on.
18
19   It should be noted that "first" means "leftmost" for rows and
20   "topmost" for columns, and that rows and columns need not begin or
21   end with black pixels.
22
23   Example:
24                  1   1
25                  1   1
26              2 1 1 1 1 1 2 3
27              3 2 1 2 1 2 3 4 8 9
28
29        3 6   # # # . # # # # # #
30        1 4   # . . . . . # # # #
31      1 1 3   . . # . # . . # # #
32          2   . . . . . . . . # #
33        3 3   . . # # # . . # # #
34        1 4   # . . . . . # # # #
35        2 5   # # . . . # # # # #
36        2 5   # # . . . # # # # #
37        1 1   . . . # . . . . . #
38          3   . . # # # . . . . .
39
40   (In Russia such puzzles are known as "Japanese crosswords".)
41
42   References:
43   Robert A. Bosch, "Painting by Numbers", 2000.
44   <http://www.oberlin.edu/~math/faculty/bosch/pbn-page.html> */
45
46/*--------------------------------------------------------------------*/
47/* Main part based on the formulation proposed by Robert Bosch. */
48
49param m, integer, >= 1;
50/* the number of rows */
51
52param n, integer, >= 1;
53/* the number of columns */
54
55param row{i in 1..m, 1..n div 2}, integer, >= 0, default 0;
56/* the cluster-size sequence for row i (raw data) */
57
58param col{j in 1..n, 1..m div 2}, integer, >= 0, default 0;
59/* the cluster-size sequence for column j (raw data) */
60
61param kr{i in 1..m} := sum{t in 1..n div 2: row[i,t] > 0} 1;
62/* the number of clusters in row i */
63
64param kc{j in 1..n} := sum{t in 1..m div 2: col[j,t] > 0} 1;
65/* the number of clusters in column j */
66
67param sr{i in 1..m, t in 1..kr[i]} := row[i,t], integer, >= 1;
68/* the cluster-size sequence for row i */
69
70param sc{j in 1..n, t in 1..kc[j]} := col[j,t], integer, >= 1;
71/* the cluster-size sequence for column j */
72
73check{i in 1..m}: sum{t in 1..kr[i]} sr[i,t] <= n - (kr[i] - 1);
74/* check that the sum of the cluster sizes in each row is valid */
75
76check{j in 1..n}: sum{t in 1..kc[j]} sc[j,t] <= m - (kc[j] - 1);
77/* check that the sum of the cluster sizes in each column is valid */
78
79check: sum{i in 1..m, t in 1..kr[i]} sr[i,t] =
80       sum{j in 1..n, t in 1..kc[j]} sc[j,t];
81/* check that the sum of the cluster sizes in all rows is equal to the
82   sum of the cluster sizes in all columns */
83
84param er{i in 1..m, t in 1..kr[i]} :=
85   if t = 1 then 1 else er[i,t-1] + sr[i,t-1] + 1;
86/* the smallest value of j such that row i's t-th cluster can be
87   placed in row i with its leftmost pixel occupying pixel j */
88
89param lr{i in 1..m, t in 1..kr[i]} :=
90   if t = kr[i] then n + 1 - sr[i,t] else lr[i,t+1] - sr[i,t] - 1;
91/* the largest value of j such that row i's t-th cluster can be
92   placed in row i with its leftmost pixel occupying pixel j */
93
94param ec{j in 1..n, t in 1..kc[j]} :=
95   if t = 1 then 1 else ec[j,t-1] + sc[j,t-1] + 1;
96/* the smallest value of i such that column j's t-th cluster can be
97   placed in column j with its topmost pixel occupying pixel i */
98
99param lc{j in 1..n, t in 1..kc[j]} :=
100   if t = kc[j] then m + 1 - sc[j,t] else lc[j,t+1] - sc[j,t] - 1;
101/* the largest value of i such that column j's t-th cluster can be
102   placed in column j with its topmost pixel occupying pixel i */
103
104var z{i in 1..m, j in 1..n}, binary;
105/* z[i,j] = 1, if row i's j-th pixel is painted black
106   z[i,j] = 0, if row i's j-th pixel is painted white */
107
108var y{i in 1..m, t in 1..kr[i], j in er[i,t]..lr[i,t]}, binary;
109/* y[i,t,j] = 1, if row i's t-th cluster is placed in row i with its
110                 leftmost pixel occupying pixel j
111   y[i,t,j] = 0, if not */
112
113var x{j in 1..n, t in 1..kc[j], i in ec[j,t]..lc[j,t]}, binary;
114/* x[j,t,i] = 1, if column j's t-th cluster is placed in column j with
115                 its topmost pixel occupying pixel i
116   x[j,t,i] = 0, if not */
117
118s.t. fa{i in 1..m, t in 1..kr[i]}:
119     sum{j in er[i,t]..lr[i,t]} y[i,t,j] = 1;
120/* row i's t-th cluster must appear in row i exactly once */
121
122s.t. fb{i in 1..m, t in 1..kr[i]-1, j in er[i,t]..lr[i,t]}:
123     y[i,t,j] <= sum{jp in j+sr[i,t]+1..lr[i,t+1]} y[i,t+1,jp];
124/* row i's (t+1)-th cluster must be placed to the right of its t-th
125   cluster */
126
127s.t. fc{j in 1..n, t in 1..kc[j]}:
128     sum{i in ec[j,t]..lc[j,t]} x[j,t,i] = 1;
129/* column j's t-th cluster must appear in column j exactly once */
130
131s.t. fd{j in 1..n, t in 1..kc[j]-1, i in ec[j,t]..lc[j,t]}:
132     x[j,t,i] <= sum{ip in i+sc[j,t]+1..lc[j,t+1]} x[j,t+1,ip];
133/* column j's (t+1)-th cluster must be placed below its t-th cluster */
134
135s.t. fe{i in 1..m, j in 1..n}:
136     z[i,j] <= sum{t in 1..kr[i], jp in er[i,t]..lr[i,t]:
137                   j-sr[i,t]+1 <= jp and jp <= j} y[i,t,jp];
138/* the double coverage constraint stating that if row i's j-th pixel
139   is painted black, then at least one of row i's clusters must be
140   placed in such a way that it covers row i's j-th pixel */
141
142s.t. ff{i in 1..m, j in 1..n}:
143     z[i,j] <= sum{t in 1..kc[j], ip in ec[j,t]..lc[j,t]:
144                   i-sc[j,t]+1 <= ip and ip <= i} x[j,t,ip];
145/* the double coverage constraint making sure that if row i's j-th
146   pixel is painted black, then at least one of column j's clusters
147   covers it */
148
149s.t. fg{i in 1..m, j in 1..n, t in 1..kr[i], jp in er[i,t]..lr[i,t]:
150     j-sr[i,t]+1 <= jp and jp <= j}: z[i,j] >= y[i,t,jp];
151/* the constraint to prevent white pixels from being covered by the
152   row clusters */
153
154s.t. fh{i in 1..m, j in 1..n, t in 1..kc[j], ip in ec[j,t]..lc[j,t]:
155     i-sc[j,t]+1 <= ip and ip <= i}: z[i,j] >= x[j,t,ip];
156/* the constraint to prevent white pixels from being covered by the
157   column clusters */
158
159/* this is a feasibility problem, so no objective is needed */
160
161/*--------------------------------------------------------------------*/
162/* The following part is used only to check for multiple solutions. */
163
164param zz{i in 1..m, j in 1..n}, binary, default 0;
165/* zz[i,j] is z[i,j] for a previously found solution */
166
167s.t. fz{1..1 : sum{i in 1..m, j in 1..n} zz[i,j] > 0}:
168     sum{i in 1..m, j in 1..n}
169         (if zz[i,j] then (1 - z[i,j]) else z[i,j]) >= 1;
170/* the constraint to forbid finding a solution, which is identical to
171   the previously found one; this constraint is included in the model
172   only if the previously found solution specified by the parameter zz
173   is provided in the data section */
174
175solve;
176
177/*--------------------------------------------------------------------*/
178/* Print solution to the standard output. */
179
180for {i in 1..m}
181{  printf{j in 1..n} " %s", if z[i,j] then "#" else ".";
182   printf "\n";
183}
184
185/*--------------------------------------------------------------------*/
186/* Write solution to a text file in PostScript format. */
187
188param ps, symbolic, default "solution.ps";
189
190printf "%%!PS-Adobe-3.0\n" > ps;
191printf "%%%%Creator: GLPK (pbn.mod)\n" >> ps;
192printf "%%%%BoundingBox: 0 0 %d %d\n",
193       6 * (n + 2), 6 * (m + 2) >> ps;
194printf "%%%%EndComments\n" >> ps;
195printf "<</PageSize [%d %d]>> setpagedevice\n",
196       6 * (n + 2), 6 * (m + 2) >> ps;
197printf "0.1 setlinewidth\n" >> ps;
198printf "/A { 2 copy 2 copy 2 copy newpath moveto exch 6 add exch line" &
199       "to\n" >> ps;
200printf "exch 6 add exch 6 add lineto 6 add lineto closepath } bind de" &
201       "f\n" >> ps;
202printf "/W { A stroke } def\n" >> ps;
203printf "/B { A fill } def\n" >> ps;
204printf {i in 1..m, j in 1..n} "%d %d %s\n",
205       (j - 1) * 6 + 6, (m - i) * 6 + 6,
206       if z[i,j] then "B" else "W" >> ps;
207printf "%%%%EOF\n" >> ps;
208
209printf "Solution has been written to file %s\n", ps;
210
211/*--------------------------------------------------------------------*/
212/* Write solution to a text file in the form of MathProg data section,
213   which can be used later to check for multiple solutions. */
214
215param dat, symbolic, default "solution.dat";
216
217printf "data;\n" > dat;
218printf "\n" >> dat;
219printf "param zz :" >> dat;
220printf {j in 1..n} " %d", j >> dat;
221printf " :=\n" >> dat;
222for {i in 1..m}
223{  printf " %2d", i >> dat;
224   printf {j in 1..n} " %s", if z[i,j] then "1" else "." >> dat;
225   printf "\n" >> dat;
226}
227printf ";\n" >> dat;
228printf "\n" >> dat;
229printf "end;\n" >> dat;
230
231printf "Solution has also been written to file %s\n", dat;
232
233/*--------------------------------------------------------------------*/
234/* The following data correspond to the example above. */
235
236data;
237
238param m := 10;
239
240param n := 10;
241
242param row : 1  2  3  :=
243      1     3  6  .
244      2     1  4  .
245      3     1  1  3
246      4     2  .  .
247      5     3  3  .
248      6     1  4  .
249      7     2  5  .
250      8     2  5  .
251      9     1  1  .
252      10    3  .  .
253;
254
255param col : 1  2  3  4  :=
256      1     2  3  .  .
257      2     1  2  .  .
258      3     1  1  1  1
259      4     1  2  .  .
260      5     1  1  1  1
261      6     1  2  .  .
262      7     2  3  .  .
263      8     3  4  .  .
264      9     8  .  .  .
265      10    9  .  .  .
266;
267
268end;
Note: See TracBrowser for help on using the repository browser.