【洛谷P3386】二分圖最大匹配之Kuhn算法/匈牙利算法:直觀理解

題目:洛谷P3386 【模板】二分圖最大匹配

🥕 匈牙利算法本來是針對帶權圖最大匹配的,這里由于題目只是求最大匹配的邊數,所以我們也只考慮無權的情況。

🚀 本文旨在服務于看了別的關于匈牙利算法的文章但不甚理解的童鞋,希望能夠從直觀上幫大家看清算法的巧妙之處,而關于匈牙利算法的歷史淵源、主要流程等則不再贅述。


關于二分圖最大匹配我們可以有如下直觀的理解:我們假設有一群人和一堆物品,我們知道每個人喜歡這些物品中的哪幾個,每個人可以分得一個物品,求最多可以使多少人分到自己喜歡的某個物品。我們令人的編號是ABCDEF……,物品的編號是123456……。

?? 那好啊,我們先看人A。他喜歡哪個物品,我們就先給他唄,隨便選一個他喜歡的就行(當然如果他什么都不喜歡就只能晾在一邊了)。設分給A的物品為x。
?? 再看人B。如果他喜歡一個沒有分配給A的物品y,那太好了,把y分配給B即可。但如果B只喜歡已經分配給A的那個物品x,B就只能空手而歸了嗎?也不盡然。我們可以把x從A手里搶過來給B,然后看給A能不能分配一個別的。如果A除了喜歡x以外還喜歡一個物品z,那就好辦了,把z給A,把x給B,皆大歡喜。但如果A也喜歡物品x,那沒辦法,A和B勢必只能滿足一個,沒法在把x給B的情況下給A補償一個別的,所以只能委屈一下B了。

……

?? 不管考慮哪個人(設為P),我們總是可以遵循以下套路:如果P喜歡的物品中有未被認領的,那么直接分配給P就好了;否則就依次檢查P喜歡的物品,如果可以想辦法讓這個物品的持有者Q另覓他物,那就把這個物品搶過來;如果他搶奪所有喜歡的物品都失敗了(即物品持有者都沒法騰出來),那他就分不到物品了。此外,在令當前物品持有者Q更換物品的時候,也需要進行相同的流程:如果Q喜歡的物品當中還有其他未被占領的,就分配給Q,否則也是看他喜歡的物品除了給P的之外有沒有可能讓持有者騰出來。那么,這就形成了一個遞歸結構,如果某一層的人獲得了未被占領的物品,那么就P就被分配到了物品;如果所有輾轉騰挪的方法都無效,那么P就不會獲得物品。注意:遞歸過程中需要維護一個棧,用于標記哪些人缺物品,每次遞歸需要把發起人壓入棧中;往更深遞歸時不能再回頭向這些棧中的人索要物品(這個由一個vis數組實現)不然的話會形成回路,即A向B要物品,B向C要物品,C向D要物品,D又向棧中的B要物品,B又向C要……就無限循環了

?? 所以,什么時候一個(我們所考慮的)新人能獲得一個心儀的物品呢?那就是:要么直接有一個他喜愛的物品未被占有;要么他向一個人P要一個他喜愛的物品,這個人P向另一個人Q要,Q又向R要,……,直到Y向Z要,且Z恰好可以重新占有一個他喜愛的空閑物品的時候(前者可看作后者的一個特殊情況)。這個鏈條就是大名鼎鼎的增廣路,可以表示為:(比如)A-1-B-2-C-3-…-H-8。增廣路一定有奇數條邊,其中A-1的含義是“人A想要搶奪物品1”,1-B的含義是“因為B目前占有物品1,所以他被逼著找別的物品”;最后H-8代表“終于,人H找到了另一個心愛的物品8,其中8目前是空閑狀態”。如果有一個人A找到了由他開始的一條增廣路,那么他就可以分配到物品。上面提到的遞歸過程就是一個尋找可行增廣路的過程。

?? 總結一下,目前的算法流程是:按任意順序掃描每個人,對于每個人通過遞歸方法求出他能不能通過找到一條從他開始的增廣路來分配到一個物品;在遞歸過程中不能回過頭問棧中的人要物品,因為他們本來就是缺物品的狀態,如果這樣會產生無限循環。

🤔 但是,這樣的算法實現出來在洛谷上會有三個點TLE。有一個值得注意的細節是:我們只是禁止向遞歸棧中的人索取物品,但是如果一條遞歸路徑走投無路回溯的時候,一些人會被彈出棧外。這意味著遞歸過程可能訪問同一個人多次(因為他被彈出棧后就可以變成被訪問了;換言之,vis數組中他可能重新被標記為未訪問狀態)。

😕 你可能會問:難道我們可以只訪問每個人一次嗎?難道不同的棧格局訪問同一個人,是否能形成合法增廣路的結果是一樣的嗎? ——說實話,這個問題我想了很久。

💡 還真是。在我后面的代碼中,每次掃描到新的人重置一遍vis數組,且遞歸到某個人直接令vis[他]=true,如果已經為true則直接返回增廣路尋找失敗。這是我覺得這個算法最玄妙的地方——如果在某個棧格局下尋找以人P開始的增廣路失敗,那我們在后面就直接放棄這個人了(即便在另一個棧格局下從P開始可能會成功)。那么我們自然要問:這樣不會漏掉一些可行的增廣路,導致算法在本該返回成功的時候返回失敗嗎?

🌈 想要證明不會漏掉情況,就要證明:如果有一個被vis[P]=true排除掉的成功增廣路 β \beta β,那么一定存在另一個不含P的成功增廣路 α \alpha α,使得我們的算法可以發現該增廣路 α \alpha α并返回成功。(這樣我們就能繞開“P被禁止訪問了”這個限制。)在這之前,我們還因為在棧格局 γ \gamma γ(即一個殘缺的、失敗的增廣路)下遇到P無功而返,而把P標記為不可訪問(就算這時把vis[P]標記為true的)。這個失敗的棧格局 γ \gamma γ也非常重要。
首先, β \beta β中遇到P后并未無功而返,而是成功找到了后續的增廣路。那為什么在 γ \gamma γ下卻失敗了呢?只有一種解釋: β \beta β中有P后面的人在 γ \gamma γ的前綴中出現過,但在 γ \gamma γ下因為不能回頭問棧中的人要物品所以就不可訪問了。
現在,我要開始施展魔法了:取 β \beta β中P后面最后一個出現在 γ \gamma γ前綴中的人T,并把 γ \gamma γ中T后面的部分替換為 β \beta β中T后面的部分,得到 α \alpha α。那么這個 α \alpha α一定是合法增廣路:因為 β \beta β中T后面的部分都沒有在 γ \gamma γ前綴中出現過的人了(畢竟T已經是最后一個出現的了),而 α \alpha α繼承了 γ \gamma γ的一部分前綴和 β \beta β的一部分后綴,所以 α \alpha α中并沒有同一個人被訪問兩次的情況。況且 α \alpha α是不含P的,所以我們的算法不會把 α \alpha α排除出去。
當然你可能會說:如果 α \alpha α也因為另一個人Q被標記為vis[Q]=true而被排除了呢?那就把 α \alpha α當作新的 β \beta β,“排除Q的失敗增廣路前綴”為新的 γ \gamma γ,重復前后綴拼接的操作,……直到迭代了若干次后的 α \alpha α沒有被排除為止。至此,我們便證明了每個人在每一輪中只被訪問一次的合理性。這也就保證了算法的復雜度為 O ( ∣ V ∣ ∣ E ∣ ) O(|V||E|) O(V∣∣E)(即人數與邊數之積):每個人被掃描到;每次掃描一個人,可以保證所有的人都只被訪問一次,而每次訪問可能會檢查他所有喜歡的物品(即他的所有邊),故每次掃描至多訪問所有邊。


💻 代碼實現:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>using namespace std;const int MAXN = 1005;
vector<int> e[MAXN];
int n, m, t, match[MAXN], vis[MAXN];bool dfs(int u)
{if(vis[u]) return false;vis[u] = true;for(int it = 0; it < e[u].size(); ++it){int v = e[u][it];if(!match[v] || dfs(match[v])){match[v] = u;return true;}}return false;
}int solve()
{int ans = 0;for(int i = 1; i <= n; ++i){memset(vis, '\0', sizeof(vis));ans += dfs(i);}return ans;
}int main()
{cin >> n >> m >> t;int u, v;while(t--){cin >> u >> v;e[u].push_back(v);}cout << solve() << endl;return 0;
}

參考:https://cp-algorithms.com/graph/kuhn_maximum_bipartite_matching.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81594.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81594.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81594.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【數據結構】二分查找(返回插入點)5.14

二分查找基礎版 package 二分查找; public class BinarySearch { public static void main(String[] args) { // TODO Auto-generated method stub } public static int binarySearchBasic(int[] a,int target) { int i0,ja.length-1; //設置指針初值 while…

Ubuntu 命令

Ubuntu 命令速查表? ?分類??命令??功能描述??示例/常用選項????文件與目錄?ls列出目錄內容ls -a&#xff08;顯示隱藏文件&#xff09;; ls -lh&#xff08;詳細列表易讀大小&#xff09; cd切換目錄cd ~&#xff08;主目錄&#xff09;; cd ..&#xff08;上級…

Java集合框架詳解與使用場景示例

Java集合框架是Java標準庫中一組用于存儲和操作數據的接口和類。它提供了多種數據結構&#xff0c;每種數據結構都有其特定的用途和性能特點。在本文中&#xff0c;我們將詳細介紹Java集合框架的主要組成部分&#xff1a;List、Set和Queue&#xff0c;并通過代碼示例展示它們的…

《Python星球日記》 第78天:CV 基礎與圖像處理

名人說:路漫漫其修遠兮,吾將上下而求索。—— 屈原《離騷》 創作者:Code_流蘇(CSDN)(一個喜歡古詩詞和編程的Coder??) 目錄 一、計算機視覺(CV)簡介1. 什么是計算機視覺?2. 計算機視覺的應用場景3. 圖像的基本屬性a》像素(Pixel)b》通道(Channel)c》分辨率(Res…

LabVIEW在電子電工教學中的應用

在電子電工教學領域&#xff0c;傳統教學模式面臨諸多挑戰&#xff0c;如實驗設備數量有限、實驗過程存在安全隱患、教學內容更新滯后等。LabVIEW 作為一款功能強大的圖形化編程軟件&#xff0c;為解決這些問題提供了創新思路&#xff0c;在電子電工教學的多個關鍵環節發揮著重…

【優選算法 | 字符串】字符串模擬題精選:思維+實現解析

算法相關知識點可以通過點擊以下鏈接進行學習一起加油&#xff01;雙指針滑動窗口二分查找前綴和位運算模擬鏈表哈希表 在眾多字符串算法題中&#xff0c;有一類題目看起來沒有太多算法技巧&#xff0c;卻經常讓人“翻車”——那就是字符串模擬題。這類題型往往不依賴復雜的數據…

虛幻引擎5-Unreal Engine筆記之Default Pawn與GamMode、Camera的關系

虛幻引擎5-Unreal Engine筆記之Default Pawn與GamMode、Camera的關系 code review! 文章目錄 虛幻引擎5-Unreal Engine筆記之Default Pawn與GamMode、Camera的關系1.Default Pawn與Camera的關系1.1. Default Pawn 是什么&#xff1f;1.2. Default Pawn 的主要組件1.3. Default…

HarmonyOs開發之———UIAbility進階

謝謝關注!! 前言:上一篇文章主要介紹開發之———使用HTTP訪問網絡資源:HarmonyOs開發之———使用HTTP訪問網絡資源-CSDN博客 代碼資源:https://download.csdn.net/download/this_is_bug/90841580 一、基本概念 UIAbility 是 HarmonyOS 應用的核心組件,負責用戶界面的…

java實現根據Velocity批量生成pdf并合成zip壓縮包

Velocity 模版操作 用的之前寫好的: 傳送門 其中需要新加一個轉成輸入流的方法 public static InputStream convertToPdf(StringWriter stringWriter) throws IOException {//將 HTML 轉為字節流byte[] htmlBytes stringWriter.toString().getBytes(StandardCharsets.UTF_8)…

SCDN能夠運用在物聯網加速當中嗎?

在當今的科技化時代當中&#xff0c;物聯網已經廣泛滲透在各個領域行業當中&#xff0c;隨著物聯網規模的不斷擴大&#xff0c;數據信息的傳輸速度和網絡穩定性成為企業需要重視的兩點因素&#xff0c;而SCDN也成為安全內容分發網絡作為一種融合了內容加速和安全防護的技術&…

二程運輸的干散貨船路徑優化

在二程運輸中&#xff0c;干散貨船需要將貨物從一個港口運輸到多個不同的目的地港口。路徑優化的目標是在滿足貨物運輸需求、船舶航行限制等條件下&#xff0c;確定船舶的最佳航行路線&#xff0c;以最小化運輸成本、運輸時間或其他相關的優化目標。 影響因素 港口布局與距離…

Oracle物理恢復相關注意點

如果需要恢復的數據庫或者數據文件不存在&#xff0c;則需要將全量備份集RESTORE[ 將全量備份集恢復到目標數據庫中&#xff0c;稱之為RESTORE。]到目標數據庫中&#xff0c;然后再RECOVER[ 將增量備份集或者歸檔日志恢復到目標數據庫中&#xff0c;稱之為RECOVER。]增量備份集…

C++ string小記

#include<string> using std::string;string s1; string s2 "hello" //初始化一個hello字符串 string s3(5,a) //連續5個字符a組成的串&#xff0c;即aaaaa///字符串操作int length s1.size() //.size()求字符串長度char c1 s1[1]; //從下標0開始&#xf…

自然語言處理入門級項目——文本分類(預處理)

文章目錄 前言1.數據預處理1.1數據集介紹1.2數據集抽取1.3劃分數據集1.4數據清洗1.5數據保存 2.樣本的向量化表征2.1詞匯表2.2向量化2.3自定義數據集2.4備注 結語 前言 本篇博客主要介紹自然語言處理領域中一個項目案例——文本分類&#xff0c;具體而言就是判斷評價屬于積極還…

C++面試2——C與C++的關系

C與C++的關系及核心區別的解析 一、哲學與編程范式:代碼組織的革命 過程式 vs 多范式混合 C語言是過程式編程的典范,以算法流程為中心,強調“怎么做”(How)。例如,實現鏈表操作需手動管理節點指針和內存。 C++則是多范式語言,支持面向對象(OOP)、泛型編程(模板)、函…

HTTP與HTTPS協議的核心區別

HTTP與HTTPS協議的核心區別 數據傳輸安全性 HTTP采用明文傳輸&#xff0c;數據易被竊聽或篡改&#xff08;如登錄密碼、支付信息&#xff09;&#xff0c;而HTTPS通過SSL/TLS協議對傳輸內容加密&#xff0c;確保數據完整性并防止中間人攻擊。例如&#xff0c;HTTPS會生成對稱加…

PotPlayer 安裝 madVR、LAV Filters 以提升解碼能力和視頻音頻效果

PotPlayer自帶的解碼器并不是最好&#xff0c;如下兩張截圖都是出自 TOP GUN: Maverick 較暗、灰蒙蒙的一張&#xff0c;是安裝插件之前明亮的一張&#xff0c;是安裝插件之后 詳細安裝參考 https://www.bilibili.com/video/BV1UV5qzuE74?spm_id_from333.788.videopod.sectio…

深入理解 OpenCV 的 DNN 模塊:從基礎到實踐

在計算機視覺領域蓬勃發展的當下&#xff0c;深度學習模型的廣泛應用推動著技術的不斷革新。OpenCV 作為一款強大且開源的計算機視覺庫&#xff0c;其 DNN&#xff08;Deep Neural Network&#xff09;模塊為深度學習模型的落地應用提供了高效便捷的解決方案。本文將以理論為核…

Spring MVC 中請求處理流程及核心組件解析

在 Spring MVC 中&#xff0c;請求從客戶端發送到服務器后&#xff0c;需要經過一系列組件的處理才能最終到達具體的 Controller 方法。這個過程涉及多個核心組件和復雜的映射機制&#xff0c;下面詳細解析其工作流程&#xff1a; 1. 核心組件與請求流程 Spring MVC 的請求處…

RISC-V 開發板 MUSE Pi Pro V2D圖像加速器測試,踩坑介紹

視頻講解&#xff1a; RISC-V 開發板 MUSE Pi Pro V2D圖像加速器測試&#xff0c;踩坑介紹 今天測試下V2D&#xff0c;這是K1特有的硬件級別的2D圖像加速器&#xff0c;參考如下文檔&#xff0c;但文檔中描述的部分有不少問題&#xff0c;后面會講下 https://bianbu-linux.spa…