competitive/combinatorial_optimization/
maximum_scoring_segment_sets.rs1use super::{DoublyLinkedList, Zero};
2use std::{
3 cmp::{Ordering, Reverse},
4 ops::{Add, Neg},
5};
6
7pub fn maximum_scoring_segment_sets<T>(scores: &[T]) -> Vec<T>
9where
10 T: Clone + Ord + Zero + Add<Output = T> + Neg<Output = T>,
11{
12 let n = scores.len();
13 let zero = T::zero();
14 let mut positive_sum = zero.clone();
15 let mut nonnegative_count = 0;
16 let mut negative_scores = Vec::with_capacity(n);
17 let mut blocks: Vec<T> = Vec::with_capacity(n);
18
19 for score in scores {
20 let ordering = score.cmp(&zero);
21 match ordering {
22 Ordering::Greater => {
23 positive_sum = positive_sum + score.clone();
24 nonnegative_count += 1;
25 }
26 Ordering::Equal => {
27 nonnegative_count += 1;
28 continue;
29 }
30 Ordering::Less => negative_scores.push(Reverse(score.clone())),
31 }
32
33 if let Some(last) = blocks.last_mut()
34 && ((*last).cmp(&zero) == Ordering::Greater) == (ordering == Ordering::Greater)
35 {
36 *last = last.clone() + score.clone();
37 } else {
38 blocks.push(score.clone());
39 }
40 }
41
42 if blocks.first().is_some_and(|score| score < &zero) {
43 blocks.remove(0);
44 }
45 if blocks.last().is_some_and(|score| score < &zero) {
46 blocks.pop();
47 }
48
49 let mut result = vec![zero.clone(); n + 1];
50 let segment_count = blocks.len().div_ceil(2);
51
52 for value in &mut result[segment_count..=nonnegative_count] {
53 value.clone_from(&positive_sum);
54 }
55
56 negative_scores.sort_unstable();
57 let mut score_with_negatives = positive_sum.clone();
58 for (offset, Reverse(score)) in negative_scores.into_iter().enumerate() {
59 score_with_negatives = score_with_negatives + score;
60 result[nonnegative_count + offset + 1].clone_from(&score_with_negatives);
61 }
62
63 if segment_count <= 1 {
64 return result;
65 }
66
67 let block_count = blocks.len();
68 let mut links = DoublyLinkedList::new(block_count);
69 for index in 1..block_count {
70 links.link(index - 1, index);
71 }
72
73 let absolute = |value: &T| {
74 if value < &zero {
75 -value.clone()
76 } else {
77 value.clone()
78 }
79 };
80 let mut weights: Vec<_> = blocks.iter().map(|block| Some(absolute(block))).collect();
81 let mut candidates: Vec<_> = (0..block_count).collect();
82
83 let mut losses = Vec::with_capacity(segment_count);
84 for _ in 1..segment_count {
85 let (index, left, right) = loop {
86 let index = candidates
87 .pop()
88 .expect("a live block sequence must have a local minimum");
89 if weights[index].is_none() {
90 continue;
91 }
92 let left = links.prev(index);
93 let right = links.next(index);
94 if (left == usize::MAX || weights[index] <= weights[left])
95 && (right == usize::MAX || weights[index] <= weights[right])
96 {
97 break (index, left, right);
98 }
99 };
100 losses.push(weights[index].take().expect("a local minimum must be live"));
101
102 if left == usize::MAX {
103 links.detach(index);
104 let (_, next) = links.detach(right);
105 weights[right] = None;
106 candidates.push(next);
107 } else if right == usize::MAX {
108 let (prev, _) = links.detach(left);
109 links.detach(index);
110 weights[left] = None;
111 candidates.push(prev);
112 } else {
113 blocks[index] = blocks[left].clone() + blocks[index].clone() + blocks[right].clone();
114 weights[index] = Some(absolute(&blocks[index]));
115 let (prev, _) = links.detach(left);
116 let (_, next) = links.detach(right);
117 weights[left] = None;
118 weights[right] = None;
119 candidates.extend(
120 [prev, index, next]
121 .into_iter()
122 .filter(|&index| index != usize::MAX),
123 );
124 }
125 }
126
127 losses.extend(weights.into_iter().flatten());
128 losses.sort_unstable();
129
130 let mut current_score = positive_sum;
131 for (result, loss) in result[..segment_count].iter_mut().rev().zip(losses) {
132 current_score = current_score + -loss;
133 result.clone_from(¤t_score);
134 }
135 result
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::{rand, tools::Xorshift};
142
143 fn naive(scores: &[i32]) -> Vec<i32> {
144 let n = scores.len();
145 let mut result = vec![i32::MIN; n + 1];
146 result[0] = 0;
147 for mask in 1usize..1 << n {
148 let mut score = 0;
149 let mut selected_count = 0;
150 let mut run_count = 0;
151 for (index, &value) in scores.iter().enumerate() {
152 if mask >> index & 1 == 1 {
153 score += value;
154 selected_count += 1;
155 if index == 0 || mask >> (index - 1) & 1 == 0 {
156 run_count += 1;
157 }
158 }
159 }
160 for value in &mut result[run_count..=selected_count] {
161 *value = (*value).max(score);
162 }
163 }
164 result
165 }
166
167 #[test]
168 fn test_maximum_scoring_segment_sets() {
169 for n in 0..=6u32 {
170 for mut encoded in 0..5usize.pow(n) {
171 let mut scores = Vec::with_capacity(n as usize);
172 for _ in 0..n {
173 scores.push((encoded % 5) as i32 - 2);
174 encoded /= 5;
175 }
176 assert_eq!(maximum_scoring_segment_sets(&scores), naive(&scores));
177 }
178 }
179
180 const Q: usize = 200;
181 const N: usize = 15;
182 const A: i32 = 1000;
183 let mut rng = Xorshift::default();
184 for _ in 0..Q {
185 rand!(rng, n: 7..=N, scores: [-A..=A; n]);
186 assert_eq!(maximum_scoring_segment_sets(&scores), naive(&scores));
187 }
188 }
189}