problemreductions/models/misc/
rectilinear_picture_compression.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
10use crate::traits::Problem;
11use serde::de::Deserializer;
12use serde::{Deserialize, Serialize};
13
14type Rectangle = (usize, usize, usize, usize);
15
16inventory::submit! {
17 ProblemSchemaEntry {
18 name: "RectilinearPictureCompression",
19 display_name: "Rectilinear Picture Compression",
20 aliases: &[],
21 dimensions: &[],
22 module_path: module_path!(),
23 description: "Cover all 1-entries of a binary matrix with at most K axis-aligned all-1 rectangles",
24 fields: &[
25 FieldInfo { name: "matrix", type_name: "Vec<Vec<bool>>", description: "m x n binary matrix" },
26 FieldInfo { name: "bound", type_name: "i64", description: "Maximum number of rectangles allowed" },
27 ],
28 }
29}
30
31#[derive(Debug, Clone, Serialize)]
62pub struct RectilinearPictureCompression {
63 matrix: Vec<Vec<bool>>,
64 bound: i64,
65 #[serde(skip)]
66 maximal_rects: Vec<Rectangle>,
67}
68
69impl<'de> Deserialize<'de> for RectilinearPictureCompression {
70 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
71 where
72 D: Deserializer<'de>,
73 {
74 #[derive(Deserialize)]
75 struct Inner {
76 matrix: Vec<Vec<bool>>,
77 bound: i64,
78 }
79 let inner = Inner::deserialize(deserializer)?;
80 Ok(Self::new(inner.matrix, inner.bound))
81 }
82}
83
84impl RectilinearPictureCompression {
85 pub fn new(matrix: Vec<Vec<bool>>, bound: i64) -> Self {
91 assert!(!matrix.is_empty(), "Matrix must not be empty");
92 let cols = matrix[0].len();
93 assert!(cols > 0, "Matrix must have at least one column");
94 assert!(
95 matrix.iter().all(|row| row.len() == cols),
96 "All rows must have the same length"
97 );
98 let mut instance = Self {
99 matrix,
100 bound,
101 maximal_rects: Vec::new(),
102 };
103 instance.maximal_rects = instance.compute_maximal_rectangles();
104 instance
105 }
106
107 pub fn num_rows(&self) -> usize {
109 self.matrix.len()
110 }
111
112 pub fn num_cols(&self) -> usize {
114 self.matrix[0].len()
115 }
116
117 pub fn bound(&self) -> i64 {
119 self.bound
120 }
121
122 pub fn matrix(&self) -> &[Vec<bool>] {
124 &self.matrix
125 }
126
127 pub fn maximal_rectangles(&self) -> &[Rectangle] {
132 &self.maximal_rects
133 }
134
135 fn build_prefix_sum(&self) -> Vec<Vec<usize>> {
136 let m = self.num_rows();
137 let n = self.num_cols();
138 let mut prefix_sum = vec![vec![0; n + 1]; m + 1];
139
140 for r in 0..m {
141 let mut row_sum = 0;
142 for c in 0..n {
143 row_sum += usize::from(self.matrix[r][c]);
144 prefix_sum[r + 1][c + 1] = prefix_sum[r][c + 1] + row_sum;
145 }
146 }
147
148 prefix_sum
149 }
150
151 fn range_is_all_ones(
152 prefix_sum: &[Vec<usize>],
153 r1: usize,
154 c1: usize,
155 r2: usize,
156 c2: usize,
157 ) -> bool {
158 let area = (r2 - r1 + 1) * (c2 - c1 + 1);
159 let sum = prefix_sum[r2 + 1][c2 + 1] + prefix_sum[r1][c1]
160 - prefix_sum[r1][c2 + 1]
161 - prefix_sum[r2 + 1][c1];
162 sum == area
163 }
164
165 fn compute_maximal_rectangles(&self) -> Vec<Rectangle> {
170 let m = self.num_rows();
171 let n = self.num_cols();
172
173 let mut candidates = Vec::new();
176 for r1 in 0..m {
177 for c1 in 0..n {
178 if !self.matrix[r1][c1] {
179 continue;
180 }
181 let mut c_max = n;
183 for c in c1..n {
184 if !self.matrix[r1][c] {
185 c_max = c;
186 break;
187 }
188 }
189 let mut c_end = c_max; for r2 in r1..m {
192 let mut new_c_end = c1;
194 for c in c1..c_end {
195 if self.matrix[r2][c] {
196 new_c_end = c + 1;
197 } else {
198 break;
199 }
200 }
201 if new_c_end <= c1 {
202 break;
203 }
204 c_end = new_c_end;
205 candidates.push((r1, c1, r2, c_end - 1));
206 }
207 }
208 }
209
210 candidates.sort();
212 candidates.dedup();
213
214 let prefix_sum = self.build_prefix_sum();
217 candidates
218 .into_iter()
219 .filter(|&(r1, c1, r2, c2)| {
220 let can_extend_left =
221 c1 > 0 && Self::range_is_all_ones(&prefix_sum, r1, c1 - 1, r2, c1 - 1);
222 let can_extend_right =
223 c2 + 1 < n && Self::range_is_all_ones(&prefix_sum, r1, c2 + 1, r2, c2 + 1);
224 let can_extend_up =
225 r1 > 0 && Self::range_is_all_ones(&prefix_sum, r1 - 1, c1, r1 - 1, c2);
226 let can_extend_down =
227 r2 + 1 < m && Self::range_is_all_ones(&prefix_sum, r2 + 1, c1, r2 + 1, c2);
228
229 !(can_extend_left || can_extend_right || can_extend_up || can_extend_down)
230 })
231 .collect()
232 }
233}
234
235impl Problem for RectilinearPictureCompression {
236 const NAME: &'static str = "RectilinearPictureCompression";
237 type Value = crate::types::Or;
238
239 fn variant() -> Vec<(&'static str, &'static str)> {
240 crate::variant_params![]
241 }
242
243 fn dims(&self) -> Vec<usize> {
244 vec![2; self.maximal_rects.len()]
245 }
246
247 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
248 crate::types::Or({
249 let rects = &self.maximal_rects;
250 if config.len() != rects.len() {
251 return crate::types::Or(false);
252 }
253 if config.iter().any(|&v| v >= 2) {
254 return crate::types::Or(false);
255 }
256
257 let selected_count: usize = config.iter().sum();
259 if (selected_count as i64) > self.bound {
260 return crate::types::Or(false);
261 }
262
263 let m = self.num_rows();
265 let n = self.num_cols();
266 let mut covered = vec![vec![false; n]; m];
267 for (i, &x) in config.iter().enumerate() {
268 if x == 1 {
269 let (r1, c1, r2, c2) = rects[i];
270 for row in &mut covered[r1..=r2] {
271 for cell in &mut row[c1..=c2] {
272 *cell = true;
273 }
274 }
275 }
276 }
277
278 for (row_m, row_c) in self.matrix.iter().zip(covered.iter()) {
279 for (&entry, &cov) in row_m.iter().zip(row_c.iter()) {
280 if entry && !cov {
281 return crate::types::Or(false);
282 }
283 }
284 }
285
286 true
287 })
288 }
289}
290
291crate::declare_variants! {
292 default RectilinearPictureCompression => "2^(num_rows * num_cols)",
293}
294
295#[cfg(feature = "example-db")]
296pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
297 vec![crate::example_db::specs::ModelExampleSpec {
298 id: "rectilinear_picture_compression",
299 instance: Box::new(RectilinearPictureCompression::new(
303 vec![
304 vec![true, true, false, false],
305 vec![true, true, false, false],
306 vec![false, false, true, true],
307 vec![false, false, true, true],
308 ],
309 2,
310 )),
311 optimal_config: vec![1, 1],
312 optimal_value: serde_json::json!(true),
313 }]
314}
315
316#[cfg(test)]
317#[path = "../../unit_tests/models/misc/rectilinear_picture_compression.rs"]
318mod tests;