diff --git a/Cargo.lock b/Cargo.lock index 7c3680f..0ff3dcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,6 +159,7 @@ dependencies = [ "http-body-util", "hyper-util", "iso8601", + "rand 0.9.1", "serde", "serde_json", "sha2", @@ -1069,7 +1070,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand", + "rand 0.8.5", "smallvec", "zeroize", ] @@ -1161,7 +1162,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1268,8 +1269,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1279,7 +1290,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1291,6 +1312,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "redox_syscall" version = "0.5.12" @@ -1313,7 +1343,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core", + "rand_core 0.6.4", "signature", "spki", "subtle", @@ -1464,7 +1494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -1629,7 +1659,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand", + "rand 0.8.5", "rsa", "serde", "sha1", @@ -1667,7 +1697,7 @@ dependencies = [ "md-5", "memchr", "once_cell", - "rand", + "rand 0.8.5", "serde", "serde_json", "sha2", diff --git a/Cargo.toml b/Cargo.toml index 9912617..e608fe5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,4 @@ hex = "0.4.3" sha2 = "0.10.8" chrono = "0.4.33" iso8601 = { version = "0.6.2", features = ["chrono", "serde"] } +rand = "0.9.1" \ No newline at end of file diff --git a/src/app/algorithm.rs b/src/app/algorithm.rs index b03461b..2f08ffc 100644 --- a/src/app/algorithm.rs +++ b/src/app/algorithm.rs @@ -1,137 +1,506 @@ use std::cmp::min; use std::collections::VecDeque; -#[derive(Debug, Clone)] -struct Edge { - to: usize, - capacity: i32, - flow: i32, - rev: usize, +#[derive(Clone, Debug)] +pub struct Edge { + pub u: i32, + pub next: usize, + pub v: i32, + pub w: i32, + pub c: i32, } -#[derive(Debug, Clone)] -struct Dinic { - graph: Vec>, - n: usize, +pub struct Mcmf { + pub n: i32, + pub m: i32, + pub s: i32, + pub t: i32, + pub maxflow: i32, + pub cost: i32, + pub in_field: i32, + pub ss: i32, + pub tt: i32, + pub a0: i32, + pub a1: i32, + pub d: Vec, + pub incf: Vec, + pub pre: Vec, + pub head: Vec, + pub a: Vec, + pub vis: Vec, + pub e: Vec, + pub q: VecDeque, } -impl Dinic { - fn new(n: usize) -> Self { - Dinic { - graph: vec![Vec::new(); n], - n, - } - } +impl Mcmf { + pub const INF: i32 = 0x3f3f3f3f; - fn add_edge(&mut self, from: usize, to: usize, capacity: i32) { - let to_len = self.graph[to].len(); - let from_len = self.graph[from].len(); - self.graph[from].push(Edge { - to, - capacity, - flow: 0, - rev: to_len, + pub fn new() -> Self { + let mut m = Mcmf { + n: 0, + m: 0, + s: 0, + t: 0, + maxflow: 0, + cost: 0, + in_field: 0, + ss: 0, + tt: 0, + a0: 0, + a1: 0, + d: Vec::new(), + incf: Vec::new(), + pre: Vec::new(), + head: Vec::new(), + a: Vec::new(), + vis: Vec::new(), + e: Vec::new(), + q: VecDeque::new(), + }; + m.e.push(Edge { + u: 0, + next: 0, + v: 0, + w: 0, + c: 0, }); - self.graph[to].push(Edge { - to: from, - capacity: 0, - flow: 0, - rev: from_len, + m.e.push(Edge { + u: 0, + next: 0, + v: 0, + w: 0, + c: 0, }); + m + } + + pub fn init(&mut self) { + self.n = 0; + self.m = 0; + self.s = 0; + self.t = 0; + self.maxflow = 0; + self.cost = 0; + self.in_field = 0; + self.ss = 0; + self.tt = 0; + self.a0 = 0; + self.a1 = 0; + self.d.clear(); + self.incf.clear(); + self.pre.clear(); + self.head.clear(); + self.a.clear(); + self.vis.clear(); + self.e.truncate(2); + self.q.clear(); + } + + pub fn set_n(&mut self, n: i32) { + let sz = (n + 3) as usize; + self.d.resize(sz, 0); + self.incf.resize(sz, 0); + self.pre.resize(sz, 0); + self.head.resize(sz, 0); + self.a.resize(sz, 0); + self.vis.resize(sz, false); + } + + fn add(&mut self, u: i32, v: i32, w: i32, c: i32) { + let idx = self.e.len(); + self.e.push(Edge { + u, + next: self.head[u as usize], + v, + w, + c, + }); + self.head[u as usize] = idx; + } + + fn add_e(&mut self, u: i32, v: i32, w: i32, c: i32) { + self.add(u, v, w, c); + self.add(v, u, 0, -c); } - fn bfs(&self, s: usize, t: usize, level: &mut [i32]) -> bool { - level.fill(-1); - level[s] = 0; - let mut queue = VecDeque::new(); - queue.push_back(s); + pub fn add_edge(&mut self, u: i32, v: i32, l: i32, d: i32, c: i32) { + self.a[v as usize] += l; + self.a[u as usize] -= l; + self.add_e(u, v, d - l, c); + } + + pub fn add_signed(&mut self, u: i32, v: i32, w: i32, c: i32) { + if c >= 0 { + self.add_edge(u, v, 0, w, c); + } else { + self.a[v as usize] += w; + self.a[u as usize] -= w; + self.add_edge(v, u, 0, w, -c); + self.a1 += c * w; + } + } - while let Some(u) = queue.pop_front() { - for edge in &self.graph[u] { - if edge.capacity - edge.flow > 0 && level[edge.to] == -1 { - level[edge.to] = level[u] + 1; - queue.push_back(edge.to); + fn spfa(&mut self) -> bool { + self.q.clear(); + self.vis.fill(false); + self.d.fill(Self::INF); + self.q.push_back(self.ss); + self.d[self.ss as usize] = 0; + self.incf[self.ss as usize] = std::i32::MAX; + while let Some(u) = self.q.pop_front() { + self.vis[u as usize] = false; + let mut i = self.head[u as usize]; + while i != 0 { + let e = &self.e[i]; + if e.w > 0 && self.d[u as usize] + e.c < self.d[e.v as usize] { + self.d[e.v as usize] = self.d[u as usize] + e.c; + self.pre[e.v as usize] = i; + self.incf[e.v as usize] = min(self.incf[u as usize], e.w); + if !self.vis[e.v as usize] { + self.vis[e.v as usize] = true; + self.q.push_back(e.v); + } } + i = e.next; } } - level[t] != -1 + self.d[self.tt as usize] != Self::INF } - fn dfs( - &mut self, - u: usize, - t: usize, - level: &Vec, - flow: i32, - start: &mut Vec, - ) -> i32 { - if u == t { - return flow; - } - while start[u] < self.graph[u].len() { - let i = start[u]; - let (capacity, to, rev) = { - let edge = &self.graph[u][i]; - (edge.capacity - edge.flow, edge.to, edge.rev) - }; + /// 沿增广路更新流与费用 + fn update(&mut self) { + let mut x = self.tt; + let flow = self.incf[self.tt as usize]; + while x != self.ss { + let i = self.pre[x as usize]; + self.e[i].w -= flow; + let ri = i ^ 1; + self.e[ri].w += flow; + x = self.e[ri].v; + } + self.maxflow += flow; + self.cost += self.d[self.tt as usize] * flow; + } - if capacity > 0 && level[to] == level[u] + 1 { - let pushed = self.dfs(to, t, level, min(flow, capacity), start); - if pushed > 0 { - self.graph[u][i].flow += pushed; - self.graph[to][rev].flow -= pushed; - return pushed; - } - } - start[u] += 1; + /// 主循环:不断 SPFA + 更新 + pub fn work(&mut self) { + while self.spfa() { + self.update(); } - 0 } - fn max_flow(&mut self, s: usize, t: usize) -> i32 { - let mut total_flow = 0; - let mut level = vec![0; self.n]; - while self.bfs(s, t, &mut level) { - let mut start = vec![0; self.n]; - while let Some(flow) = Some(self.dfs(s, t, &level, i32::MAX, &mut start)) { - if flow == 0 { - break; - } - total_flow += flow; + /// 求解 + pub fn solve(&mut self) { + // 构造超级源汇 + self.ss = self.n + 1; + self.tt = self.n + 2; + for i in 1..=self.n { + let ai = self.a[i as usize]; + if ai > 0 { + self.add_edge(self.ss, i, 0, ai, 0); + } else if ai < 0 { + self.add_edge(i, self.tt, 0, -ai, 0); } } - total_flow + // 保证原图中循环流可行 + self.add_edge(self.tt, self.ss, 0, Self::INF, 0); + self.work(); + + // 切换为真正的源汇 + self.ss = self.s; + self.tt = self.t; + self.a1 += self.cost; + self.maxflow = 0; + self.cost = 0; + // 将最后两条边容量置零 + let len = self.e.len(); + self.e[len - 1].w = 0; + self.e[len - 2].w = 0; + self.work(); + self.a0 += self.maxflow; + self.a1 += self.cost; } } -pub fn max_flow(users: Vec<(i64, Vec)>, spares: Vec) -> Vec> { - let mut dinic = Dinic::new(users.len() * 7 + spares.len() + 2); - let s = users.len() * 7 + spares.len(); - let t = users.len() * 7 + spares.len() + 1; - let mut user_nodes = Vec::new(); - for (user_id, stamps) in users { - let n = spares.len() + user_nodes.len(); - for day in 0..7 { - user_nodes.push(user_id); - dinic.add_edge(n + day, t, 1); - } - for stamp in stamps { - let user_node = n + spares[stamp]; - dinic.add_edge(stamp, user_node, 1); +use std::error::Error; + +#[derive(Clone, Debug)] +pub struct User { + pub id: u64, + pub stamps: Vec, +} + +#[derive(Clone, Debug)] +pub struct Spare { + pub stamp: u64, + pub day: u64, +} + +pub struct Distribution { + user: Vec, + spare: Vec, + mf: Mcmf, +} + +impl Distribution { + pub fn new() -> Self { + Distribution { + user: Vec::new(), + spare: Vec::new(), + mf: Mcmf::new(), } } - for stamp in 0..spares.len() { - dinic.add_edge(s, stamp, 1); + + pub fn init( + &mut self, + users: &[User], + spares: &[Spare], + spare_size: usize, + ) -> Result<(), Box> { + if spare_size != spares.len() { + return Err("spare_size != spares.len()".into()); + } + self.user.clear(); + self.spare.clear(); + self.user.reserve(users.len()); + self.spare.reserve(spare_size); + self.user.extend_from_slice(users); + self.spare.extend_from_slice(spares); + Ok(()) } - dinic.max_flow(s, t); - (0..spares.len()) - .map(|stamp| { - for edge in dinic.graph[stamp].iter() { - if edge.flow > 0 { - return Some(user_nodes[edge.to - spares.len()]); - } + + pub fn solve(&mut self) -> Vec { + self.mf.init(); + let s = 1; + let n_nodes = (self.user.len() * 8) as i32 + self.spare.len() as i32 + 2; + let t = n_nodes; + self.mf.n = n_nodes; + self.mf.s = s; + self.mf.t = t; + self.mf.set_n(n_nodes); + + for i in 0..self.user.len() { + let u = (i + 2) as i32; + self.mf.add_edge(s, u, 0, 1, 20); + self.mf.add_edge(s, u, 0, 1, 50); + self.mf.add_edge(s, u, 0, 1, 100); + } + for day in 1..=7 { + for j in 0..self.user.len() { + let from = (j + 2) as i32; + let to = (j + 2 + day * self.user.len()) as i32; + self.mf.add_edge(from, to, 0, 1, 0); + } + } + for (i, user) in self.user.iter().enumerate() { + for &stamp in &user.stamps { + let from = + (i as u64 + 2 + (self.spare[stamp as usize].day + 1) * self.user.len() as u64) + as i32; + let to = + n_nodes - self.spare.len() as i32 + self.spare[stamp as usize].stamp as i32; + self.mf.add_edge(from, to, 0, 1, 0); + } + } + for i in 0..self.spare.len() { + let from = n_nodes - self.spare.len() as i32 + i as i32; + self.mf.add_edge(from, t, 0, 1, 0); + } + self.mf.solve(); + let a0 = 1 + self.user.len() as i64; + let a1 = 1 + (self.user.len() * 8) as i64; + let a2 = (self.mf.n - 1) as i64; + let mut res = vec![ + User { + id: 0, + stamps: Vec::new() + }; + self.user.len() + ]; + for e in &self.mf.e { + let u = e.u as i64; + let v = e.v as i64; + if u > a0 && u <= a1 && v > a1 && v <= a2 && e.w == 0 { + let idx = ((u - a0 - 1) % self.user.len() as i64) as usize; + let stamp = (v - a1 - 1) as u64; + res[idx].stamps.push(stamp); } - None + } + for (i, user) in self.user.iter().enumerate() { + res[i].id = user.id; + res[i].stamps.sort_unstable(); + } + res + } +} + +fn distribute(users: Vec, spares: Vec) -> Vec { + let mut sol = Distribution::new(); + sol.init(&users, &spares, spares.len()) + .expect("spare_size must equal spares.len()"); + sol.solve() +} + +pub fn max_flow( + entries: Vec<(i64, Vec)>, + spares: Vec, +) -> Vec> { + let users: Vec = entries + .into_iter() + .map(|(id, stamps)| User { + id: id as u64, + stamps: stamps.into_iter().map(|s| s as u64).collect(), + }) + .collect(); + + let sp_len = spares.len(); + + let spare_structs: Vec = spares + .into_iter() + .enumerate() + .map(|(idx, day)| Spare { + stamp: idx as u64, + day: day as u64, }) - .collect() + .collect(); + + let assigned = distribute(users, spare_structs); + + let mut res = vec![None; sp_len]; + for user in assigned { + let uid = user.id as i64; + for stamp in user.stamps { + let idx = stamp as usize; + if idx < sp_len { + res[idx] = Some(uid); + } + } + } + res } + + +#[cfg(test)] +mod tests { + use super::*; + + use rand::{rng, seq::SliceRandom}; + #[test] + fn test_mcmf_sample1() { + let mut mf = Mcmf::new(); + mf.n = 4; + mf.s = 4; + mf.t = 3; + mf.set_n(4); + + mf.add_signed(4, 2, 30, 2); + mf.add_signed(4, 3, 20, 3); + mf.add_signed(2, 3, 20, 1); + mf.add_signed(2, 1, 30, 9); + mf.add_signed(1, 3, 40, 5); + + mf.solve(); + + assert_eq!(mf.a0, 50, "期望最大流 a0 = 50,但实际是 {}", mf.a0); + assert_eq!(mf.a1, 280, "期望最小费用 a1 = 280,但实际是 {}", mf.a1); + } + #[test] + fn test_mcmf_sample2() { + let mut mf = Mcmf::new(); + mf.n = 5; + mf.s = 1; + mf.t = 5; + mf.set_n(5); + + mf.add_signed(1, 3, 2, 4); + mf.add_signed(1, 2, 2, 3); + mf.add_signed(3, 5, 2, 2); + mf.add_signed(3, 2, 1, -1); + mf.add_signed(2, 4, 2, -2); + mf.add_signed(4, 3, 1, -1); + mf.add_signed(4, 5, 1, 3); + + mf.solve(); + + assert_eq!(mf.a0, 3, "期望最大流 a0 = 3,但实际是 {}", mf.a0); + assert_eq!(mf.a1, 12, "期望最小费用 a1 = 12,但实际是 {}", mf.a1); + } + // empty_test + #[test] + fn test_distribution_sample1() { + let users = vec![User { + id: 0, + stamps: vec![], + }]; + let spares = vec![Spare { day: 0, stamp: 0 }]; + let res = distribute(users, spares); + println!("res: {:?}", res); + assert_eq!(res.len(), 1, "期望结果长度为 1,但实际是 {}", res.len()); + } + #[test] + fn test_distribution_sample2() { + let users = vec![User { + id: 0, + stamps: vec![0], + }]; + let spares = vec![Spare { day: 0, stamp: 0 }]; + let res = distribute(users, spares); + // println!("res: {:?}", res); + assert_eq!(res.len(), 1, "期望结果长度为 1,但实际是 {}", res.len()); + } + + #[test] + fn test_distribution_sample3() { + let users = vec![User { + id: 0, + stamps: vec![0, 1], + }]; + let spares = vec![Spare { day: 0, stamp: 0 }, Spare { day: 0, stamp: 1 }]; + let res = distribute(users, spares); + // println!("res: {:?}", res); + assert_eq!(res.len(), 1, "期望结果长度为 1,但实际是 {}", res.len()); + } + + #[test] + fn test_distribution_sample4() { + let mut spares = Vec::new(); + for day in 0..7 { + for slot in 0..2 { + spares.push(Spare { + stamp: (day * 2 + slot) as u64, + day: day as u64, + }); + } + } + + let mut rng = rng(); + let all_slots: Vec = (0..14).collect(); + let users: Vec = (0..10) + .map(|i| { + let mut picks = all_slots.clone(); + picks.shuffle(&mut rng); + picks.truncate(5); + User { id: i as u64, stamps: picks } + }) + .collect(); + + println!("初始"); + for user in &users { + println!(" 用户 {}: 时隙 {:?}", user.id, user.stamps); + } + let result = distribute(users.clone(), spares.clone()); + + println!("分配结果:"); + for user in &result { + println!(" 用户 {}: 时隙 {:?}", user.id, user.stamps); + } + + assert_eq!(result.len(), users.len(), "结果用户数量应与输入一致"); + for user in result { + assert!(user.stamps.len() <= 5, "用户分配时隙不能超过 5"); + for &stamp in &user.stamps { + assert!(stamp < 14, "时隙索引应在 0..14 范围内"); + } + } + } +} \ No newline at end of file diff --git a/src/app/spare.rs b/src/app/spare.rs index 11fbb72..fe2de88 100644 --- a/src/app/spare.rs +++ b/src/app/spare.rs @@ -617,8 +617,8 @@ mod test { end_time: String::from("P0Y0M0DT10H0M0S"), room: String::from("room1"), assignee: Some(User { - id: 1, - username: String::from("testuser"), + id: 2, + username: String::from("testadmin"), }), }, Spare {