競プロで見るKőnigの定理

## 概要
二部グラフ上での最小頂点被覆のサイズが最大マッチングのサイズと等しいというのがKőnig(ケーニヒ)の定理です．
https://en.wikipedia.org/wiki/K%C5%91nig%27s_theorem_%28graph_theory%29

具体的な問題を通してこの定理に触れたいと思います．

## 問題
USACOのAsteroidsという問題を例にして考えます．
http://poj.org/problem?id=3041
https://www.acmicpc.net/problem/1867

### 問題概要
N×Nマスのグリッド(1 ≦ N ≦ 500)があり，その上にK個の石(1 ≦ K ≦ 10,000)があります．
あなたは一回の操作で行または列を一つ選び，その行または列に存在する石をすべて取り除くことができます．
すべての石を取り除くための最小の操作回数を求めてください．

### 具体例
`X`を石のあるマス，`.`を空きマスとし，以下のような状態を考えます．

```
X...
X.X.
XXXX
X...
```

このケースだと，以下のように行と列を選択することで最小回数を達成できます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825073/6d004415-2b94-8c24-1322-1e59baade47f.png)

## 二部グラフ

ここで，行を上から1, 2, 3, 4，列を左からA, B, C, Dと名前を付けます．
そして行と列を頂点に，石を辺に置き換えたグラフを作ります(例えば，左上の石は(1, A)にあるので1 - Aを結びます)．
これは二部グラフとなっています．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825073/7cf698cb-3dc3-48e2-6886-9303fcfd2fb3.png)

## 頂点被覆
グラフからいくつかの頂点を選択した集合をVとすると，**すべての辺**の両端のどちらかがVに含まれる場合，Vは**頂点被覆である**といいます．
「選んだ頂点から出ている辺を塗り，すべての辺を塗れているかどうか」というイメージです．

具体例を通して見てみましょう．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825073/f0f7686c-deb6-bbaf-e38d-491a4db0ca0c.png)

左のものは頂点{3, A}を選びましたが，(2, C)の辺をカバーしていません．
これは頂点被覆ではありません．

真ん中のものは頂点{2, 3, A}を選び，すべての辺をカバーしています．
そして，これが頂点数最小であり，最小の頂点被覆を**最小頂点被覆**と呼びます．唯一ではなく，例えば{3, A, C}を選んだ場合でも最小頂点被覆となります．

右のものは頂点{1, 2, 3, 4, A, B, C}を選び，すべての辺をカバーしています．
頂点被覆ではありますが，最小ではありません．

## 問題との対応
このグラフの導入は唐突に思えますが，問題との対応を見てみましょう．

操作として，行か列を選択するというのは**頂点を選択する**ことと対応し，そのライン上の石を取り除くというのは**その頂点から出ている辺を塗る**ということに対応づきます．

例えば，
2行目を選択する -> 頂点2を選択する
石(2, A)と(2, C)が除去される -> 辺(2, A)と(2, C)が塗られる
ことと対応します．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825073/00bb5d5a-f346-151f-05af-178231af9854.png)


その上で，すべての石を取り除くというのは**すべての辺を塗る**ことと対応し，操作回数の最小化というのは**選択した頂点数の最小化**に対応づきます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3825073/99dd8e23-982b-c25f-dc0f-36bb8d253a7f.png)


これでこの問題を最小頂点被覆問題に帰着させることができました．

## Kőnigの定理
Kőnigの定理により，**二部グラフ上での**最小頂点被覆のサイズと最大マッチングのサイズが等しいことが言えます．

具体的な証明は以下を参照してください．とても分かりやすくまとまっています．

[競プロで使う Dilworth の定理](https://miscalc.hatenablog.com/entry/2022/11/11/213943#%E4%BA%8C%E9%83%A8%E3%82%B0%E3%83%A9%E3%83%95%E3%81%A7%E3%81%AE%E6%9C%80%E5%A4%A7%E3%83%9E%E3%83%83%E3%83%81%E3%83%B3%E3%82%B0%E3%81%A8%E6%9C%80%E5%B0%8F%E7%82%B9%E8%A2%AB%E8%A6%86%E3%81%AE%E9%96%A2%E4%BF%82K%C5%91nig-%E3%81%AE%E5%AE%9A%E7%90%86)
[二部グラフの最小点被覆、最大安定集合 (最大独立集合)、最小辺被覆を総整理！](https://qiita.com/drken/items/7f98315b56c95a6181a4)
[Kőnig の定理と最大流最小カット定理](https://null-mn.hatenablog.com/entry/2022/10/31/223403)

一般のグラフでの最小頂点被覆問題はNP-hardのようです．[^1]

## 提出
二部グラフでの最大マッチングは最大流で解けます．詳しくは以下を参照してください．
[‪実世界で超頻出！二部マッチング (輸送問題、ネットワークフロー問題）の解法を総整理！‬](https://qiita.com/drken/items/e805e3f514acceb87602)

最終的に最大流問題に帰着することができました．以下が提出コードです．
https://www.acmicpc.net/source/86519044

<details><summary>提出コード</summary>

```cpp
#include <bits/stdc++.h>
using namespace std;

template<typename flow_t>
struct Dinic{
    struct Edge{
        int from, to, rev;
        flow_t cap;
        bool is_rev;
        Edge(int f, int t, int r, flow_t c, bool b) : from(f), to(t), rev(r), cap(c), is_rev(b) {}
    };

    vector<vector<Edge>> G;
    vector<int> dist;
    vector<int> iter;
    const flow_t INF = numeric_limits<flow_t>::max();

    Dinic(int N) : G(N), dist(N), iter(N) {}

    void add_edge(int from, int to, flow_t cap) {
        int fromrev = G[from].size();
        int torev = G[to].size();
        G[from].push_back(Edge(from, to, torev, cap, 0));
        G[to].push_back(Edge(to, from, fromrev, 0, 1));
    }

    void bfs(int s) {
        fill(dist.begin(), dist.end(), -1);
        dist[s] = 0;
        queue<int> q;
        q.push(s);
        while(q.size()) {
            int v = q.front(); q.pop();
            for(const Edge& e: G[v]) {
                if(e.cap == 0 || dist[e.to] >= 0) continue;
                dist[e.to] = dist[v] + 1;
                q.push(e.to);
            }
        }
    }

    flow_t dfs(int v, int t, flow_t f) {
        if(v == t) return f;
        if(f == 0) return 0;

        for(int& i = iter[v]; i < (int)G[v].size(); i++) {
            Edge& e = G[v][i];
            if(e.cap == 0 || dist[v] >= dist[e.to]) continue;
            flow_t flow = dfs(e.to, t, min(f, e.cap));
            if(flow == 0) continue;

            e.cap -= flow;
            G[e.to][e.rev].cap += flow;

            return flow;
        }
        return 0;
    }

    flow_t max_flow(int s, int t) {
        flow_t ret = 0;

        while(true) {
            bfs(s);
            if(dist[t] < 0) return ret;

            fill(iter.begin(), iter.end(), 0);
            while(true) {
                flow_t flow = dfs(s, t, INF);
                if(flow == 0) break;
                ret += flow;
            }
        }

        return 0;
    }

    vector<Edge> edges() {
        vector<Edge> ret;
        for(const auto& v : G) {
            for(const auto& e : v) {
                if(e.is_rev) continue;
                ret.push_back(e);
            }
        }
        return ret;
    }

    void debug() {
        for(const auto& v : G) {
            for(const auto& e : v) {
                if(e.is_rev) continue;
                cerr << e.from << " -> " << e.to << " (flow : " << G[e.to][e.rev].cap << " / "
                    << e.cap + G[e.to][e.rev].cap << ")" << endl;
            }
        }
    }
};


int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int N, K;
    cin >> N >> K;

    Dinic<int> dinic(2 * N + 2);
    int s = 2 * N, t = 2 * N + 1;
    
    for(int i = 0; i < K; i++) {
        int a, b;
        cin >> a >> b;
        a--; b--;
        dinic.add_edge(a, N + b, 1);
    }

    for(int i = 0; i < N; i++) {
        dinic.add_edge(s, i, 1);
        dinic.add_edge(N + i, t, 1);
    }

    cout << dinic.max_flow(s, t) << "\n";

    return 0;
}
```

</details>


## 類題
https://atcoder.jp/contests/abc274/tasks/abc274_g
https://yukicoder.me/problems/no/1479

## 最後に
Kőnigの定理について問題を通して紹介しました．

何かあればご連絡ください．

## 参考
https://codeforces.com/blog/entry/105697
https://en.wikipedia.org/wiki/K%C5%91nig%27s_theorem_%28graph_theory%29
https://byeo.tistory.com/entry/boj1867-%EB%8F%8C%EB%A7%B9%EC%9D%B4-%EC%A0%9C%EA%B1%B0


[^1]:https://www.slideshare.net/wata_orz/ss-12131479