use crate::instance::Instance;
use crate::solution::{RouteInfo, fmax};
pub fn apply_two_opt(inst: &Instance, routes: &mut [Vec<usize>], infos: &mut [RouteInfo]) -> bool {
let nr = routes.len();
let mut any_improved = false;
let mut pos = vec![usize::MAX; inst.n + 1];
for ri in 0..nr {
if infos[ri].arrival.len() != routes[ri].len() {
infos[ri].compute(inst, &routes[ri]);
}
loop {
let route = &routes[ri];
let n = route.len();
if n < 5 {
break;
}
for (idx, &v) in route.iter().enumerate() {
if v != 0 && v <= inst.n {
pos[v] = idx;
}
}
let mut best_delta = -1e-10;
let mut best_i = 0;
let mut best_j = 0;
for i in 0..n - 3 {
for j in (i + 3)..n {
let entering = route[j - 1];
if entering != 0 && entering <= inst.n && !inst.is_pickup(entering) {
let pickup = inst.pickup_of(entering);
if pickup != 0 {
let pp = pos[pickup];
if pp > i && pp <= j - 2 {
break; }
}
}
let delta = inst.dist(route[i], route[j - 1])
+ inst.dist(route[i + 1], route[j])
- inst.dist(route[i], route[i + 1])
- inst.dist(route[j - 1], route[j]);
if delta < best_delta {
if check_two_opt_feasible(inst, route, &infos[ri], i, j) {
best_delta = delta;
best_i = i;
best_j = j;
}
}
}
}
for &v in &routes[ri] {
if v != 0 && v <= inst.n {
pos[v] = usize::MAX;
}
}
if best_delta < -1e-10 {
routes[ri][best_i + 1..best_j].reverse();
infos[ri].compute(inst, &routes[ri]);
any_improved = true;
} else {
break;
}
}
}
any_improved
}
fn check_two_opt_feasible(
inst: &Instance,
route: &[usize],
info: &RouteInfo,
i: usize,
j: usize,
) -> bool {
let mut time = info.arrival[i];
let mut load = info.load[i];
let mut prev_node = route[i];
for k in (i + 1..j).rev() {
let curr = route[k];
time = fmax(
time + inst.svc(prev_node) + inst.dist(prev_node, curr),
inst.early(curr),
);
if time > inst.late(curr) {
return false;
}
load += inst.demand[curr];
if load > inst.capacity || load < 0 {
return false;
}
prev_node = curr;
}
let n = route.len();
if j < n {
let curr = route[j];
let new_arrival_j = fmax(
time + inst.svc(prev_node) + inst.dist(prev_node, curr),
inst.early(curr),
);
if new_arrival_j > inst.late(curr) {
return false;
}
if new_arrival_j <= info.arrival[j] + 1e-10 {
return true;
}
let time_shift = new_arrival_j - info.arrival[j];
if time_shift <= info.suffix_min_slack[j] + 1e-10 {
return true;
}
time = new_arrival_j;
prev_node = curr;
for &curr in &route[j + 1..] {
time = fmax(
time + inst.svc(prev_node) + inst.dist(prev_node, curr),
inst.early(curr),
);
if time > inst.late(curr) {
return false;
}
prev_node = curr;
}
}
true
}
pub(crate) fn is_route_feasible_with_reuse(
inst: &Instance,
route: &[usize],
state: &mut [u8],
touched: &mut Vec<usize>,
) -> bool {
if route.len() < 2 {
return false;
}
let mut load: i32 = 0;
for &node in &route[1..route.len() - 1] {
load += inst.demand[node];
if load > inst.capacity || load < 0 {
return false;
}
}
let mut time = inst.early(0);
for i in 1..route.len() {
let prev = route[i - 1];
let curr = route[i];
time = fmax(
time + inst.svc(prev) + inst.dist(prev, curr),
inst.early(curr),
);
if time > inst.late(curr) {
return false;
}
}
touched.clear();
let mut ok = true;
for &v in &route[1..route.len() - 1] {
if inst.is_pickup(v) {
if state[v] != 0 {
ok = false;
break;
}
state[v] = 1;
touched.push(v);
} else {
let p = inst.pickup_of(v);
if p == 0 || state[p] != 1 {
ok = false;
break;
}
state[p] = 2;
}
}
if ok {
for &p in touched.iter() {
if state[p] == 1 {
ok = false;
break;
}
}
}
for &p in touched.iter() {
state[p] = 0;
}
ok
}
pub fn apply_or_opt(inst: &Instance, routes: &mut [Vec<usize>], infos: &mut [RouteInfo]) -> bool {
const K_OR: usize = 4;
let nr = routes.len();
let mut any_improved = false;
let mut new_route_buf: Vec<usize> = Vec::new();
let mut precedence_state: Vec<u8> = vec![0; inst.n + 1];
let mut touched_pickups: Vec<usize> = Vec::new();
for ri in 0..nr {
loop {
let route = &routes[ri];
let n = route.len();
if n < 5 {
break;
}
let mut best_delta = -1e-10;
let mut best_seg_start = 0;
let mut best_seg_len = 0;
let mut best_insert_pos = 0;
for seg_len in 1..=K_OR {
for seg_start in 1..n - 1 {
let seg_end = seg_start + seg_len; if seg_end >= n {
break;
}
let before_seg = route[seg_start - 1];
let after_seg = route[seg_end];
let first_seg = route[seg_start];
let last_seg = route[seg_end - 1];
let removal_cost = inst.dist(before_seg, after_seg)
- inst.dist(before_seg, first_seg)
- inst.dist(last_seg, after_seg);
for insert_after in 0..n - 1 {
if insert_after >= seg_start - 1 && insert_after < seg_end {
continue;
}
let ins_before = route[insert_after];
let ins_after_node = if insert_after >= seg_start && insert_after < seg_end
{
continue;
} else if insert_after < seg_start {
route[insert_after + 1]
} else {
route[insert_after + 1]
};
let insertion_cost = inst.dist(ins_before, first_seg)
+ inst.dist(last_seg, ins_after_node)
- inst.dist(ins_before, ins_after_node);
let delta = removal_cost + insertion_cost;
if delta < best_delta {
build_or_opt_route(
route,
seg_start,
seg_end,
insert_after,
&mut new_route_buf,
);
if is_route_feasible_with_reuse(
inst,
&new_route_buf,
&mut precedence_state,
&mut touched_pickups,
) {
best_delta = delta;
best_seg_start = seg_start;
best_seg_len = seg_len;
best_insert_pos = insert_after;
}
}
}
}
}
if best_delta < -1e-10 {
build_or_opt_route(
&routes[ri],
best_seg_start,
best_seg_start + best_seg_len,
best_insert_pos,
&mut new_route_buf,
);
routes[ri].clone_from(&new_route_buf);
infos[ri].compute(inst, &routes[ri]);
any_improved = true;
} else {
break;
}
}
}
any_improved
}
fn build_or_opt_route(
route: &[usize],
seg_start: usize,
seg_end: usize,
insert_after: usize,
buf: &mut Vec<usize>,
) {
buf.clear();
if insert_after < seg_start {
buf.extend_from_slice(&route[..=insert_after]);
buf.extend_from_slice(&route[seg_start..seg_end]);
buf.extend_from_slice(&route[insert_after + 1..seg_start]);
buf.extend_from_slice(&route[seg_end..]);
} else {
buf.extend_from_slice(&route[..seg_start]);
buf.extend_from_slice(&route[seg_end..=insert_after]);
buf.extend_from_slice(&route[seg_start..seg_end]);
buf.extend_from_slice(&route[insert_after + 1..]);
}
}