haar_lib/algo/psp.rs
1//! Project Selection Problem
2
3use crate::flow::*;
4
5/// Project Selection Problem
6///
7/// # Problems
8/// - [ARC 085 E - MUL](https://atcoder.jp/contests/arc085/tasks/arc085_c)
9/// - [AOJ 3058 Ghost](http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=3058)
10/// - [AOJ 2903 Board](http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2903)
11/// - [ABC 193 F - Zebraness](https://atcoder.jp/contests/abc193/tasks/abc193_f)
12/// - [競プロ典型 90 問 040 - Get More Money](https://atcoder.jp/contests/typical90/tasks/typical90_an)
13///
14/// # References
15/// - [https://ferin-tech.hatenablog.com/entry/2019/10/28/燃やす埋める問題](https://ferin-tech.hatenablog.com/entry/2019/10/28/%E7%87%83%E3%82%84%E3%81%99%E5%9F%8B%E3%82%81%E3%82%8B%E5%95%8F%E9%A1%8C)
16/// - [https://kmyk.github.io/blog/blog/2017/12/05/minimum-cut-and-project-selection-problem/](https://kmyk.github.io/blog/blog/2017/12/05/minimum-cut-and-project-selection-problem/)
17///
18/// # Verification
19/// | function | verify |
20/// | -------- | ------ |
21/// | [`penalty_if_red`](Self::penalty_if_red) | [競プロ典型 90 問 040 #72402366](https://atcoder.jp/contests/typical90/submissions/72402366) |
22/// | [`penalty_if_blue`](Self::penalty_if_blue) | [ARC085 #27489484](https://atcoder.jp/contests/arc085/submissions/27489484) |
23/// | [`gain_if_red`](Self::gain_if_red) | [競プロ典型 90 問 040 #72402366](https://atcoder.jp/contests/typical90/submissions/72402366) |
24/// | [`gain_if_blue`](Self::gain_if_blue) | [ARC085 #27489484](https://atcoder.jp/contests/arc085/submissions/27489484) |
25/// | [`penalty_if_red_blue`](Self::penalty_if_red_blue) | |
26/// | [`penalty_if_different`](Self::penalty_if_different) | [ABC193-F #72397880](https://atcoder.jp/contests/abc193/submissions/72397880) |
27/// | [`must_be_red`](Self::must_be_red) | [ABC193-F #72397880](https://atcoder.jp/contests/abc193/submissions/72397880) |
28/// | [`must_be_blue`](Self::must_be_blue) | [ABC193-F #72397880](https://atcoder.jp/contests/abc193/submissions/72397880) |
29/// | [`if_red_then_must_be_red`](Self::if_red_then_must_be_red) | [ARC085 #27489484](https://atcoder.jp/contests/arc085/submissions/27489484) |
30/// | [`gain_if_both_red`](Self::gain_if_both_red) | |
31/// | [`gain_if_both_blue`](Self::gain_if_both_blue) | |
32
33#[derive(Clone, Debug)]
34pub struct PSP {
35 size: usize,
36 src: usize,
37 sink: usize,
38 edges: Vec<(usize, usize, u64)>,
39 node_count: usize,
40 default_gain: u64,
41}
42
43impl PSP {
44 /// `PSP`を生成する。
45 pub fn new(size: usize) -> Self {
46 Self {
47 size,
48 src: size,
49 sink: size + 1,
50 edges: vec![],
51 node_count: size + 2,
52 default_gain: 0,
53 }
54 }
55
56 /// 頂点`i`が<font color="red"><b>赤</b></font>ならば`c`の損失になる。
57 pub fn penalty_if_red(&mut self, i: usize, c: u64) {
58 assert!(i < self.size);
59 self.edges.push((i, self.sink, c));
60 }
61
62 /// 頂点`i`が<font color="blue"><b>青</b></font>ならば`c`の損失になる。
63 pub fn penalty_if_blue(&mut self, i: usize, c: u64) {
64 assert!(i < self.size);
65 self.edges.push((self.src, i, c));
66 }
67
68 /// 頂点`i`が<font color="red"><b>赤</b></font>ならば`c`の利益を得る。
69 pub fn gain_if_red(&mut self, i: usize, c: u64) {
70 assert!(i < self.size);
71 self.default_gain += c;
72 self.penalty_if_blue(i, c);
73 }
74
75 /// 頂点`i`が<font color="blue"><b>青</b></font>ならば`c`の利益を得る。
76 pub fn gain_if_blue(&mut self, i: usize, c: u64) {
77 assert!(i < self.size);
78 self.default_gain += c;
79 self.penalty_if_red(i, c);
80 }
81
82 /// 頂点`i`が<font color="red"><b>赤</b></font>かつ頂点`j`が<font color="blue"><b>青</b></font>ならば`c`の損失となる。
83 pub fn penalty_if_red_blue(&mut self, i: usize, j: usize, c: u64) {
84 assert!(i < self.size && j < self.size);
85 self.edges.push((i, j, c));
86 }
87
88 /// 頂点`i`と`j`が異なる色ならば`c`の損失となる。
89 pub fn penalty_if_different(&mut self, i: usize, j: usize, c: u64) {
90 assert!(i < self.size && j < self.size);
91 self.edges.push((i, j, c));
92 self.edges.push((j, i, c));
93 }
94
95 /// 頂点`i`は<font color="red"><b>赤</b></font>でなければならない。
96 pub fn must_be_red(&mut self, i: usize) {
97 assert!(i < self.size);
98 self.penalty_if_blue(i, u64::MAX);
99 }
100
101 /// 頂点`i`は<font color="blue"><b>青</b></font>でなければならない。
102 pub fn must_be_blue(&mut self, i: usize) {
103 assert!(i < self.size);
104 self.penalty_if_red(i, u64::MAX);
105 }
106
107 /// 頂点`i`が<font color="red"><b>赤</b></font>ならば、頂点`j`も<font color="red"><b>赤</b></font>でなければならない。
108 pub fn if_red_then_must_be_red(&mut self, i: usize, j: usize) {
109 assert!(i < self.size && j < self.size);
110 self.penalty_if_red_blue(i, j, u64::MAX);
111 }
112
113 /// 頂点`i`と`j`がともに<font color="red"><b>赤</b></font>ならば`c`の利益を得る。
114 pub fn gain_if_both_red(&mut self, i: usize, j: usize, c: u64) {
115 assert!(i < self.size && j < self.size);
116 self.default_gain += c;
117 let w = self.node_count;
118 self.node_count += 1;
119
120 self.edges.push((self.src, w, c));
121 self.edges.push((w, i, u64::MAX));
122 self.edges.push((w, j, u64::MAX));
123 }
124
125 /// 頂点`i`と`j`がともに<font color="blue"><b>青</b></font>ならば`c`の利益を得る。
126 pub fn gain_if_both_blue(&mut self, i: usize, j: usize, c: u64) {
127 assert!(i < self.size && j < self.size);
128 self.default_gain += c;
129 let w = self.node_count;
130 self.node_count += 1;
131
132 self.edges.push((w, self.sink, c));
133 self.edges.push((i, w, u64::MAX));
134 self.edges.push((j, w, u64::MAX));
135 }
136
137 /// must be制約を破った場合、`None`を返す。そうでなければ、利益の最大値を`Some`に包んで返す。
138 pub fn solve<F: MaxFlow<Cap = u64>>(self) -> Option<i64> {
139 let mut f = F::new(self.node_count);
140 for (u, v, c) in self.edges {
141 f.add_edge(u, v, c);
142 }
143
144 let flow = f.max_flow(self.src, self.sink);
145
146 if flow == u64::MAX {
147 None
148 } else {
149 Some(self.default_gain as i64 - flow as i64)
150 }
151 }
152}