Skip to content

Commit b3be820

Browse files
authored
Fix matroids (#93)
* Fix matroids * Fix F2 vector matroid
1 parent 21d22e1 commit b3be820

File tree

8 files changed

+273
-199
lines changed

8 files changed

+273
-199
lines changed

combinatorial_opt/matroid.hpp

Lines changed: 0 additions & 139 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#pragma once
2+
#include "../graph/shortest_path.hpp"
3+
#include <cassert>
4+
#include <vector>
5+
6+
// CUT begin
7+
// (Min weight) Matroid intersection solver
8+
// Algorithm based on http://dopal.cs.uec.ac.jp/okamotoy/lect/2015/matroid/
9+
// Complexity: O(CE^2 + E^3) (C : circuit query, non-weighted)
10+
template <class M1, class M2, class T = int>
11+
std::vector<bool> MatroidIntersection(M1 matroid1, M2 matroid2, std::vector<T> weights = {}) {
12+
using State = std::vector<bool>;
13+
using Element = int;
14+
assert(matroid1.size() == matroid2.size());
15+
const int M = matroid1.size();
16+
17+
for (auto &x : weights) x *= M + 1;
18+
if (weights.empty()) weights.assign(M, 0);
19+
20+
const Element gs = M, gt = M + 1;
21+
State I(M);
22+
23+
while (true) {
24+
ShortestPath<T> sssp(M + 2);
25+
matroid1.set(I);
26+
matroid2.set(I);
27+
for (int e = 0; e < M; e++) {
28+
if (I[e]) continue;
29+
auto c1 = matroid1.circuit(e), c2 = matroid2.circuit(e);
30+
if (c1.empty()) sssp.add_edge(e, gt, 0);
31+
for (Element f : c1) {
32+
if (f != e) sssp.add_edge(e, f, -weights[f] + 1);
33+
}
34+
if (c2.empty()) sssp.add_edge(gs, e, weights[e] + 1);
35+
for (Element f : c2) {
36+
if (f != e) sssp.add_edge(f, e, weights[e] + 1);
37+
}
38+
}
39+
sssp.solve(gs);
40+
auto aug_path = sssp.retrieve_path(gt);
41+
if (aug_path.empty()) break;
42+
for (auto e : aug_path) {
43+
if (e != gs and e != gt) I[e] = !I[e];
44+
}
45+
}
46+
return I;
47+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
title: (Weighted) matroid intersecition ((重みつき)マトロイド交叉)
3+
documentation_of: ./matroid_intersection.hpp
4+
---
5+
6+
マトロイド交叉(交差)問題 (matroid intersection)・共通独立集合問題とは,同じ台集合 $E$ を持つ二つのマトロイド $M_1 = (E, \mathcal{I}_1), M_2 = (E, \mathcal{I}_2)$ が与えられたとき,$X \in \mathcal{I}_1 \cap \mathcal{I}_2$ を満たす要素数最大の $X \subset E$ の一つを求めるもの.本問題は更に,重み関数 $f(e) : E \rightarrow \mathbb{R}$ が与えられたとき,要素数最大のもののうち特に $\sum_{e \in X} f(e)$ を最小化(最大化)するようなものを求める重みつき共通独立集合問題 (weighted matroid intersection problem) に一般化される.
7+
8+
本コードは,$n = |E|$,解となる集合の要素数の上界(例えば各マトロイドのランクの最小値)を $r$,マトロイドクラスのサーキットクエリ一回あたりの計算量を $c$ として,(重みなしの)マトロイド交叉を $O(nr(n + c))$ で求める.重みつきの場合は最短増加路を求めるパートが Bellman-Ford 法に置き換えられ,計算量は $O(nr(n^2 + c))$ となる(この計算量は例えば [2] のアルゴリズムを用いることで $O(nr(r + c + \log n))$ まで改善可能).
9+
10+
## 使用方法
11+
12+
`weights` を与えた場合,最小重み共通独立集合を求める.
13+
14+
```cpp
15+
UserDefinedMatroid m1, m2;
16+
vector<int> weights(M);
17+
18+
assert(m1.size() == M);
19+
assert(m2.size() == M);
20+
21+
std::vector<bool> maxindepset = MatroidIntersection(m1, m2, weights);
22+
```
23+
24+
## 問題例
25+
26+
- [Hello 2020 G. Seollal - Codeforces](https://codeforces.com/contest/1284/problem/G) グラフマトロイドと分割マトロイドの交差に帰着される.
27+
- [Deltix Round, Summer 2021 H. DIY Tree - Codeforces](https://codeforces.com/contest/1556/problem/H) グラフマトロイドと分割マトロイドの最小重み共通独立集合問題に帰着される.
28+
29+
## 文献・リンク集
30+
31+
- [1] A. Frank, "A weighted matroid intersection algorithm,"
32+
Journal of Algorithms, 2(4), 328-336, 1981.
33+
- [2] C. Brezovec, G. Cornuéjols, and F. Glover, "Two algorithms for weighted matroid intersection,"
34+
Mathematical Programming, 36(1), 39-53, 1986.
35+
- [離散最適化基礎論 (2015年度後学期) 組合せ最適化におけるマトロイドの役割](http://dopal.cs.uec.ac.jp/okamotoy/lect/2015/matroid/) とても初学者向き.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <utility>
4+
#include <vector>
5+
6+
// GraphMatroid: subgraph of undirected graphs, without loops
7+
class GraphMatroid {
8+
using Vertex = int;
9+
using Element = int;
10+
int M;
11+
int V; // # of vertices of graph
12+
std::vector<std::vector<std::pair<Vertex, Element>>> to;
13+
std::vector<std::pair<Vertex, Vertex>> edges;
14+
std::vector<Element> backtrack;
15+
std::vector<Vertex> vprev;
16+
std::vector<int> depth, root;
17+
18+
public:
19+
GraphMatroid(int V, std::vector<std::pair<Vertex, Vertex>> edges_)
20+
: M(edges_.size()), V(V), to(V), edges(edges_) {
21+
for (int e = 0; e < int(edges_.size()); e++) {
22+
assert(edges_[e].first < V and edges_[e].second < V);
23+
to[edges_[e].first].emplace_back(edges_[e].second, e);
24+
to[edges_[e].second].emplace_back(edges_[e].first, e);
25+
}
26+
}
27+
int size() const { return M; }
28+
29+
template <class State> void set(State I) {
30+
assert(int(I.size()) == M);
31+
backtrack.assign(V, -1);
32+
vprev.resize(V);
33+
depth.assign(V, -1);
34+
root.resize(V);
35+
static std::vector<Vertex> que(V);
36+
int qb, qe;
37+
for (Vertex i = 0; i < V; i++) {
38+
if (backtrack[i] >= 0) continue;
39+
que[qb = 0] = i, qe = 1, depth[i] = 0;
40+
while (qb < qe) {
41+
Vertex now = que[qb++];
42+
root[now] = i;
43+
for (auto nxt : to[now]) {
44+
if (depth[nxt.first] < 0 and I[nxt.second]) {
45+
backtrack[nxt.first] = nxt.second;
46+
vprev[nxt.first] = now;
47+
depth[nxt.first] = depth[now] + 1;
48+
que[qe++] = nxt.first;
49+
}
50+
}
51+
}
52+
}
53+
}
54+
55+
std::vector<Element> circuit(const Element e) const {
56+
assert(0 <= e and e < M);
57+
Vertex s = edges[e].first, t = edges[e].second;
58+
if (root[s] != root[t]) return {};
59+
std::vector<Element> ret{e};
60+
auto step = [&](Vertex &i) { ret.push_back(backtrack[i]), i = vprev[i]; };
61+
int ddepth = depth[s] - depth[t];
62+
for (; ddepth > 0; --ddepth) step(s);
63+
for (; ddepth < 0; ++ddepth) step(t);
64+
while (s != t) step(s), step(t);
65+
return ret;
66+
}
67+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#pragma once
2+
#include <vector>
3+
4+
struct MatroidExample {
5+
using Element = int;
6+
7+
int size() const; // # of elements of set we consider
8+
9+
// If I is independent and I + {e} is not, return elements of the circuit.
10+
// If e \in I, or I + {e} is independent, return empty vector.
11+
// If I is NOT independent, undefined.
12+
template <class State = std::vector<bool>> void set(State I);
13+
std::vector<Element> circuit(Element e) const;
14+
};
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <vector>
4+
5+
// Partition matroid (partitional matroid) : direct sum of uniform matroids
6+
class PartitionMatroid {
7+
using Element = int;
8+
int M;
9+
std::vector<std::vector<Element>> parts;
10+
std::vector<int> belong;
11+
std::vector<int> R;
12+
std::vector<int> cnt;
13+
std::vector<std::vector<Element>> circuits;
14+
15+
public:
16+
// parts: partition of [0, 1, ..., M - 1]
17+
// R: only R[i] elements from parts[i] can be chosen for each i.
18+
PartitionMatroid(int M, const std::vector<std::vector<int>> &parts_, const std::vector<int> &R_)
19+
: M(M), parts(parts_), belong(M, -1), R(R_) {
20+
assert(parts.size() == R.size());
21+
for (int i = 0; i < int(parts.size()); i++) {
22+
for (Element e : parts[i]) belong[e] = i;
23+
}
24+
for (Element e = 0; e < M; e++) {
25+
// assert(belong[e] != -1);
26+
if (belong[e] == -1) {
27+
belong[e] = parts.size();
28+
parts.push_back({e});
29+
R.push_back(1);
30+
}
31+
}
32+
}
33+
int size() const { return M; }
34+
35+
template <class State> void set(const State &I) {
36+
cnt = R;
37+
for (int e = 0; e < M; e++) {
38+
if (I[e]) cnt[belong[e]]--;
39+
}
40+
circuits.assign(cnt.size(), {});
41+
for (int e = 0; e < M; e++) {
42+
if (I[e] and cnt[belong[e]] == 0) circuits[belong[e]].push_back(e);
43+
}
44+
}
45+
46+
std::vector<Element> circuit(const Element e) const {
47+
assert(0 <= e and e < M);
48+
int p = belong[e];
49+
if (cnt[p] == 0) {
50+
auto ret = circuits[p];
51+
ret.push_back(e);
52+
return ret;
53+
}
54+
return {};
55+
}
56+
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy