1. 程式人生 > 實用技巧 >02-VTK編譯安裝

02-VTK編譯安裝

匈牙利演算法博大精深,這裡只記錄步驟。

當然,不知道這些基礎圖論的童鞋請看這裡(雖然也是草草概括一下諤諤諤)

主要步驟

\(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;
}