圖論之匹配

匹配的定義

 

給定圖 G=(V,E)G = (V, E)

一個匹配 MMGG 的一個子圖,使的其中的邊,兩兩不相交

一些名詞

未匹配點

未匹配邊

交替路徑

增廣路徑

交替路徑

 

增廣路徑

 

極大匹配

一個匹配 MM 使得無法被加入任何新的匹配

最大匹配

所有匹配中,包含最多匹配邊的那個

 

二分圖最大匹配

匈牙利演算法

對稱差集

集合 A,BA, B 的對稱差集

AB=ABABA \oplus B = A \cap B - A \cup B

兩個匹配的對稱差集

MM=PM \oplus M^* = P

  1. 孤點
  2. 交替路徑
  3. 交替環

兩個匹配的轉換

MP=MM\oplus P = M^*

MP=MM^*\oplus P = M

Berge's lemma

在圖 G=(V,E)G = (V, E) 上,

一個匹配 MM為最大匹配

若且唯若找不到增廣路徑

 

 

尋找增廣路

紀錄每個點的匹配點

從未匹配點開始 DFS

沿著交錯路徑走

找到新的未匹配點就找到了增廣路

實做

const int maxn = 500;
vector<int> edge[maxn];
int ma[maxn], T, vis[maxn], n, m;
// 1 ~ n : left, n+1 ~ m : right
bool dfs(int now) {
	if (exchange(vis[now], T) == T) return false;
	for (int u : edge[now])
		if (!ma[u] || dfs(ma[u]))
			return ma[u] = now, ma[now] = u;
	return false;
}
int get_max_matching() {
	int res = 0;
	for (int i = 1;i <= n;++i)
		res += (!ma[i] && (++T, dfs(i)));
	return res;
}

一些優化

把找擴充路徑改成 BFS
複雜度變成 O(VE)O(\sqrt{|V|}|E|)

複雜度

找擴充路徑 O(E))O(|E|))

整體複雜度 O(VE)O(|V||E|)

其他相關的事情

最大點獨立集+最小點覆蓋  |最大點獨立集| + |最小點覆蓋|   =最大邊獨立集+最小邊覆蓋=V= |最大邊獨立集| + |最小邊覆蓋| = |V|

  1. 最大點獨立集=VM|最大點獨立集| = |V| - |M|
  2. 最大邊獨立集=M|最大邊獨立集| = |M|
  3. 最小點覆蓋=M|最小點覆蓋| = |M|
  4. 最小邊覆蓋=VM|最小邊覆蓋| = |V| - |M|
  1. 最大點獨立集
  2. 最大邊獨立集
  3. 最小點覆蓋
  4. 最小邊覆蓋

 

習題

CS acadmy

 Flipping Matrtix

給一個 n x n,n103n \text{ x } n, n \leq 10^3 的 01 矩陣

每次操作可交換任兩行,或任兩列

最多做 nn 次操作,

如果有解

請輸出一組操作使的主對角線上面都是 1

 

TIOJ 1253

n x n,(n1000)n \text{ x } n , (n \leq 1000)的棋盤

上面有一些怪物

每次操作可以選一個橫排,或直排,把上面的怪物打掉

問至少要幾次操作才能消除所有的怪物

TIOJ 1601

n x m,(n,m200)n \text{ x } m , (n, m \leq 200)的鏡子上

0 代表破裂, 1 代表完好

每次操作可以交換兩個橫排,或兩個直排

可以做無限次操作。

定義一個完美的鏡子為一個鏡子使的它內部都完好

求做完操作後,週長最長的完美子鏡子

***每面輸入的鏡子最外面一整圈皆無破損,即皆為1

TIOJ 1069

外送公司接到了 n,n103n, n \leq 10^3 筆訂單

ii 筆為 xi,yi,tix_i, y_i, t_i 代表在座標 xi,yix_i, y_i 上有人會在 tit_i 的時候領取到食物(外送員必須在那裡等待)

已知在兩組座標移動的時間為他們的曼哈頓距離,外送員可帶無限多的食物,並且處理多個訂單

請問你至少要派多少外送員才能滿足所有訂單

(派出一個外送員時,他可以瞬間出現在你想要的地方)

 

一般圖匹配

縮花演算法

處理花的方法

  1. 用個 DSU 把他縮起來
    (偏麻煩
  2. 直接處理好一些資訊,讓我們可以回朔出增廣路
    (較好做

實做

 

int ma[maxn], lst[maxn], side[maxn], fid[maxn], n;
vector<int> edge[maxn];

void augment(int x, int y) {
	while (x) {
		int pa = ma[x];
		ma[x] = y, ma[y] = x;
		y = pa, x = lst[y];
	}
}

實做

 

int find_lca(int x, int y) {
	static int T, vis[maxn];
	for (++T; ; swap(x, y)) {
		if (x && exchange(vis[x], T) == T) 
			return x;
		x = fid[lst[ma[x]]];
	}
	return -1;
}
bool bfs(int rt) {
#define qpush(x) (side[x] = 0, togo.push(x))
    memset(side, -1, sizeof(side));
    queue<int> togo;
    qpush(rt);
    auto flower = [&](int x, int y, int lca) {
        while (x != lca) {
            if (side[x] == 1) qpush(x);
            lst[x] = y;
            fid[x] = fid[y] = lca;
            x = ma[x], y = lst[x];
        }
    };
    while (!togo.empty()) {
        int now = togo.front();
        togo.pop();
        for (int u : edge[now]) 
            if (side[u] == -1) {
                side[u] = 1;
                lst[u] = now;
                if (!ma[u])
                    return augment(now, u), true;
                qpush(ma[u]);
            }
            else if (!side[u]) {
                int lca = find_lca(u, now);
                flower(u, now, lca);
                flower(now, u, lca);
            }
    }
    return false;
}

int max_matching() {
	iota(fid, fid+n+1, 0);
	int res = 0;
	for (int i = 1;i <= n;++i)
		res += !ma[i] && bfs(i);
	return res;
}

實做參考來源:
日月掛長的 slides

圖論之匹配

By Kevin Zhang

圖論之匹配

  • 1,016