lemon-project-template-glpk
view deps/glpk/examples/pbn/pbn.mod @ 9:33de93886c88
Import GLPK 4.47
author | Alpar Juttner <alpar@cs.elte.hu> |
---|---|
date | Sun, 06 Nov 2011 20:59:10 +0100 |
parents | |
children |
line source
1 /* PBN, Paint-By-Numbers Puzzle */
3 /* Written in GNU MathProg by Andrew Makhorin <mao@gnu.org> */
5 /* NOTE: See also the document "Solving Paint-By-Numbers Puzzles with
6 GLPK", which is included in the GLPK distribution. */
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:
13 1. Each pixel must be blank or white.
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.
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.
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
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 . . # # # . . . . .
40 (In Russia such puzzles are known as "Japanese crosswords".)
42 References:
43 Robert A. Bosch, "Painting by Numbers", 2000.
44 <http://www.oberlin.edu/~math/faculty/bosch/pbn-page.html> */
46 /*--------------------------------------------------------------------*/
47 /* Main part based on the formulation proposed by Robert Bosch. */
49 param m, integer, >= 1;
50 /* the number of rows */
52 param n, integer, >= 1;
53 /* the number of columns */
55 param row{i in 1..m, 1..n div 2}, integer, >= 0, default 0;
56 /* the cluster-size sequence for row i (raw data) */
58 param col{j in 1..n, 1..m div 2}, integer, >= 0, default 0;
59 /* the cluster-size sequence for column j (raw data) */
61 param 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 */
64 param 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 */
67 param sr{i in 1..m, t in 1..kr[i]} := row[i,t], integer, >= 1;
68 /* the cluster-size sequence for row i */
70 param sc{j in 1..n, t in 1..kc[j]} := col[j,t], integer, >= 1;
71 /* the cluster-size sequence for column j */
73 check{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 */
76 check{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 */
79 check: 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 */
84 param 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 */
89 param 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 */
94 param 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 */
99 param 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 */
104 var 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 */
108 var 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 */
113 var 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 */
118 s.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 */
122 s.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 */
127 s.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 */
131 s.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 */
135 s.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 */
142 s.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 */
149 s.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 */
154 s.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 */
159 /* this is a feasibility problem, so no objective is needed */
161 /*--------------------------------------------------------------------*/
162 /* The following part is used only to check for multiple solutions. */
164 param 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 */
167 s.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 */
175 solve;
177 /*--------------------------------------------------------------------*/
178 /* Print solution to the standard output. */
180 for {i in 1..m}
181 { printf{j in 1..n} " %s", if z[i,j] then "#" else ".";
182 printf "\n";
183 }
185 /*--------------------------------------------------------------------*/
186 /* Write solution to a text file in PostScript format. */
188 param ps, symbolic, default "solution.ps";
190 printf "%%!PS-Adobe-3.0\n" > ps;
191 printf "%%%%Creator: GLPK (pbn.mod)\n" >> ps;
192 printf "%%%%BoundingBox: 0 0 %d %d\n",
193 6 * (n + 2), 6 * (m + 2) >> ps;
194 printf "%%%%EndComments\n" >> ps;
195 printf "<</PageSize [%d %d]>> setpagedevice\n",
196 6 * (n + 2), 6 * (m + 2) >> ps;
197 printf "0.1 setlinewidth\n" >> ps;
198 printf "/A { 2 copy 2 copy 2 copy newpath moveto exch 6 add exch line" &
199 "to\n" >> ps;
200 printf "exch 6 add exch 6 add lineto 6 add lineto closepath } bind de" &
201 "f\n" >> ps;
202 printf "/W { A stroke } def\n" >> ps;
203 printf "/B { A fill } def\n" >> ps;
204 printf {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;
207 printf "%%%%EOF\n" >> ps;
209 printf "Solution has been written to file %s\n", ps;
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. */
215 param dat, symbolic, default "solution.dat";
217 printf "data;\n" > dat;
218 printf "\n" >> dat;
219 printf "param zz :" >> dat;
220 printf {j in 1..n} " %d", j >> dat;
221 printf " :=\n" >> dat;
222 for {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 }
227 printf ";\n" >> dat;
228 printf "\n" >> dat;
229 printf "end;\n" >> dat;
231 printf "Solution has also been written to file %s\n", dat;
233 /*--------------------------------------------------------------------*/
234 /* The following data correspond to the example above. */
236 data;
238 param m := 10;
240 param n := 10;
242 param 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 ;
255 param 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 ;
268 end;