use std::{ collections::HashMap, path::{Path, PathBuf}, }; use clap::Parser; use expr::LinExpr; use grb::*; use itertools::Itertools; use ModelSense::Minimize; fn read_input(filename: impl AsRef) -> Vec> { let text = std::fs::read_to_string(filename).unwrap(); let mut r = Vec::new(); for line in text.lines().skip(1) { r.push(line.as_bytes().to_vec()); } r } #[derive(Debug, Parser)] struct Args { filename: PathBuf, outfilename: PathBuf, } fn solve(input: &[Vec]) -> String { let mut columns = Vec::new(); for i in 0..input[0].len() { let mut nextfree: u8 = 0; let mut c = Vec::new(); for l in 0..input.len() { if let Some(n) = input[0..l].iter().position(|r| r[i] == input[l][i]) { c.push(c[n]); } else { c.push(nextfree); nextfree += 1; } } columns.push(c); } let counts = columns.iter().counts(); let mut model = Model::new("model1").unwrap(); let mut variables = HashMap::new(); for (&k, &c) in &counts { let mut v = Vec::new(); let mut sum = Expr::Linear(LinExpr::new()); for i in 0..=(k.iter().max().copied().unwrap()) { let var = add_intvar!(model, name: &format!("{:?}:{}", k, i), bounds: 0..).unwrap(); sum = sum + var; v.push(var); } model.add_constr(&format!("{:?}", k), c!(sum == c)).unwrap(); variables.insert(k, v); } let d = add_intvar!(model, name: "D", bounds: 0..).unwrap(); for i in 0..input.len() { let mut sum = Expr::Linear(LinExpr::new()); for &k in counts.keys() { for j in 0..=*k.iter().max().unwrap() { if k[i] != j { sum = sum + variables[k][j as usize]; } } } model.add_constr(&format!("{:?}", i), c!(sum <= d)).unwrap(); } model.set_objective(d, Minimize).unwrap(); model.set_param(param::BarConvTol, 0.0000000001).unwrap(); model.update().unwrap(); // model.write("test.lp").unwrap(); model.optimize().unwrap(); // for vars in &variables { // for v in vars { // dbg!(model.get_obj_attr(attr::X, v).unwrap()); // } // } let mut result = String::new(); let mut hash = HashMap::new(); let mut previous = HashMap::new(); for (i, col) in columns.iter().enumerate() { if !hash.contains_key(col) { let t = model .get_obj_attr_batch(attr::X, variables[col].clone()) .unwrap() .into_iter() .map(|v| v as usize) .collect_vec(); hash.insert(col, t); } let t = hash.get(col).unwrap(); let mut p = match previous.entry(col) { std::collections::hash_map::Entry::Occupied(mut occupied_entry) => { *occupied_entry.get_mut() += 1; *occupied_entry.get() } std::collections::hash_map::Entry::Vacant(vacant_entry) => { vacant_entry.insert(0); 0 } }; let mut j = 0; while t[j] <= p { p -= t[j]; j += 1; } let l = col.iter().position(|&c| c as usize == j).unwrap(); // dbg!(i, col, t, j, l, char::from_u32(input[l][i] as u32).unwrap()); result.push(char::from_u32(input[l][i] as u32).unwrap()); } result } fn main() { let args = Args::parse(); let input = read_input(args.filename); let r = solve(&input); std::fs::write(args.outfilename, r).unwrap(); }