02-VTK編譯安裝
阿新 • • 發佈:2020-11-03
匈牙利演算法博大精深,這裡只記錄步驟。
當然,不知道這些基礎圖論的童鞋請看這裡(雖然也是草草概括一下諤諤諤)
主要步驟
\(main\)
主函式裡面一個列舉現在正在比配的左點
對於每個準備匹配的左點,進行一邊\(dfs\)
\(dfs\)有兩個引數,現在正在匹配的點 和 現在正在列舉到的點(後面說這個有什麼意義)。
\(dfs\)
每次\(dfs\)時掃描時,掃描當前左點u的所有連線的右點v。這時候,會出現兩種情況:
- 這個右點v還沒有被選過,就直接將左點和右點匹配即可,\(match[v]=u\)。
- 這個右點v已經被\(match[v]\)選過了,那麼向\(match[v]\)詢問能不能換一個對應的點來選。注意這樣搜尋時,對右點要打上訪問標記\(vis\)
那麼好了,為什麼要\(dfs\)兩個引數呢?
其實,第二個引數是用來打標記的。試想,每次列舉(不是\(dfs\))到一個點的時候,所有右點的\(vis\)就要清空一邊。那麼時間複雜度就會退化成\(O(n^2)\)的!而使用第二個引數來打標記的話,每次的標記編號更新,就意味著所有之前打的標記全部失效了。用\(O(1)\)的時間就完成了\(O(n)\)的任務!
例題
不過因為這道題的資料較小,看不出優化標記的差別呢……
Code
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #include<map> #include<set> #include<queue> #include<vector> #define IL inline #define re register #define LL long long #define ULL unsigned long long #define re register #define debug printf("Now is %d\n",__LINE__); using namespace std; template<class T>inline void read(T&x) { char ch=getchar(); while(!isdigit(ch))ch=getchar(); x=ch-'0';ch=getchar(); while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} } inline int read() { int x=0; char ch=getchar(); while(!isdigit(ch))ch=getchar(); x=ch-'0';ch=getchar(); while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x; } int G[55]; template<class T>inline void write(T x) { int g=0; if(x<0) x=-x,putchar('-'); do{G[++g]=x%10;x/=10;}while(x); for(re int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n'); } int n,m,e,ans; vector<int>L[5001]; int match[5001],vis[5001]; bool dfs(int x,int tag) { if(vis[x]==tag) return false; vis[x]=tag; for(int i=0;i<L[x].size();i++) { if(match[L[x][i]]==0||dfs(match[L[x][i]],tag)) { match[L[x][i]]=x; return true; } } return false; } int main() { n=read(); m=read(); e=read(); for(int i=1,t;i<=e;i++) t=read(),L[t].push_back(read()); for(int i=1;i<=n;i++) { if(dfs(i,i)) ans++; } cout<<ans; return 0; }