【Hopcroft Karp】HK算法简介

【还在Hungary?你Out了~】



今天上午被下面这道题虐了很久..完全没想到是二分图。

迷宫

根据藏宝地图,cj的科学家们找到了藏宝地点,但却发现入口有多个,谁都没敢冒然进入。于是大家停下脚步,想应该从哪一个门进入。突然,图形学家Mercury发现了隐藏在藏宝地图上的秘密,果然有意识呀!他发现,这个地图在暗处时会显现出一个有向无环图(至于为什么有向,我想是因为某种特殊作用力的缘故吧。),而这个图正是这个迷宫的地图。幸亏没有冒然进入,要不然就出不来了。

但是这个地图却没说出口在哪里,哎,只好用Jupiter的超级机器人进洞寻找了。每一个入口将把机器人瞬间传送到对应编号的点(所有点都能到达),为了不让Jupiter同学失去太多他心爱的机器人,我们必须用最少的机器人去搜索每个点(每个点只能被经过一次)。 

输入

第一行两个数n、m,分别表示地图的点数和边数,接下来m行,每行两个数u、v,表示有一条从点u到点v的有向边。 

输出

只有一个数,表示最少要用的机器人数。

 

输入样例

3 2

1 2

2 3

 

输出样例

1

 

数据范围

n<=200000

m<=500000


被邓大牛点醒,一看, 啊,还真是,只是这数据范围...

于是开始学习Hopcroft Karp

说起来其实也不难,

回想我们在做Hungary的时候,每次只增广一条路,如何优化呢?很简单,就是一次多增广。

流程如下:

①BFS找出极短增广路集,当最短增广路集为∅时退出

{

注意:这一句话我理解了很久,其意思实际上是对于所有未覆盖的点,预先找一条“增广路”。这条路可能实际上不增广。那么就要把原来的匹配取消。

怎么取消呢?就是这一段

else

{

     dx[my[i]] = dy[i] + 1;

que[tail++] = my[i];

}

为什么改变距离标号就可以了呢?请看下文

}


②DFS,尝试改菜的每条增广路,此事要运用一种类似于Dinic的思想——只有距离标号差为1的才能增广

就是这段

if (dy[i] == dx[x] + 1)

{

dy[i] = 0;

if (my[i] == -1 || dfs(my[i]))

{

mx[x] = i;

my[i] = x;

 return 1;

}

}

这实际上已经很像匈牙利算法了。

这样算法的复杂度降到了O(sqrt(N) * E) ,其中N为点数,E为边数,

证明援引自http://hi.baidu.com/lhdpcbjjzjhjtyr/item/0fda4d43c100ad0bc0161323

以下:

设M是一个匹配,p是关于M的一条最短增广轨,p'是关于M^p的一条最短增广轨,那么 p' >= p + 2;

证明:几乎是废话,一条增广轨的长度是奇数,两个奇数当然至少相差2。

一共只用进行O( sqrt(V) )轮。

证明:设当前的最短增广轨长度L=sqrt(n),显然到达这个长度只需要进行L/2个phase。设此时的匹配为M,而最大匹配为M*,则|M*| - |M| <= n / (sqrt(n) + 2) <= sqrt(n)。如果后续每阶段只共现一条增广轨,那么也只要sqrt(n)次就好,所以一共需要3/2sqrt(n)轮增广。

上面的证明用到了一个基本定理,即对于任意两个匹配M1和M2,如果|M1| > |M2|,则M1^M2有至少|M1| - |M2|条不相交的增广轨。

下三个分别是:Wiki版PAs和WIkiPAS翻译版WIKICPP和此题源代码

const maxn=1000;
  var dx,dy,mx,my,q:array[1..maxn]of longint;
      adj:array[1..maxn,0..maxn]of longint;
      n,m,e,i,j,ans,ff,rr:longint;
  function bfs:boolean;
    var i,u,j:longint;
    begin
      bfs:=false;
      fillchar(q,sizeof(q),0);
      rr:=1;
      ff:=1;
      for i:=1 to n do
      if mx[i]=-1
      then begin
        q[ff]:=i;
        inc(ff);
      end;
      for i:=1 to n do dx[i]:=0;
      for i:=1 to m do dy[i]:=0;
      while rr




#include 
#include
#include
#include
#include
#ifdef WIN32
#define ot "%I64d"
#else
#define ot "%lld"
#endif
#define max(a, b, t) ({t _ = (a), __ = (b); _ > __ ? _ : __;})
#define min(a, b, t) ({t _ = (a), __ = (b); _ < __ ? _ : __;})
#define maxn 1005

using namespace std;

int dx[maxn], dy[maxn], mx[maxn], my[maxn], que[maxn];
int adj[maxn][maxn];
int n, m, ans, e;

void init()
{
	freopen("hk.in", "r", stdin);
	freopen("hk.out", "w", stdout);
	scanf("%d%d%d", &n, &m, &e);
	memset(adj, 0, sizeof(adj));
	for (int i = 1; i <= e; i++)
	{
		int f, r;
		scanf("%d%d", &f, &r);
		adj[f][++adj[f][0]] = r;
	}
	memset(mx, -1, sizeof mx);
	memset(my, -1, sizeof my);
}

bool bfs()
{
	bool rt = 0;
	memset(que, 0, sizeof(que));
	int head = 1, tail = 0;
	for (int i = 1; i <= n; i++) 
		if (mx[i] == -1) que[++tail] = i;
	memset(dx, 0, sizeof(dx));
	memset(dy, 0, sizeof(dy));
	while (head <= tail);
	{
		int u = que[head++];
		for (int j = 1; j <= adj[u][0]; j++)
		{
			int i = adj[u][j];
			if (!dy[i])
			{
				dy[i] = dx[u] + 1;
				if (my[i] == -1) rt = 1;
				else
				{
					dx[my[i]] = dy[i] + 1;
					que[tail++] = my[i];
				}
			}
		}
	}
	return rt;
}

bool dfs(int x)
{
	for (int j = 1; j <= adj[x][0]; j++)
	{
		int i = adj[x][j];
		if (dy[i] == dx[x] + 1)
		{
			dy[i] = 0;
			if (my[i] == -1 || dfs(my[i]))
			{
				mx[x] = i;
				my[i] = x;
				return 1;
			}
		}
	}
	return 0;
}

int main()
{
	init();
	ans = 0;
	while (bfs())
		for (int i = 1; i <= n; i++)
			if (mx[i] == -1 && dfs(i)) ++ans;
   return 0;
}




#include
#include 
#include
#include
#include
#include
#define maxe 500005
#define maxn 200005

using namespace std;

int h[maxn], nt[maxe], t[maxe];

int mx[maxn], my[maxn], dx[maxn], dy[maxn];

int n, m, tot = 0, ans = 0;

void adde(int a, int b)
{
	t[++tot] = b; nt[tot] = h[a]; h[a] = tot;
}

void init()
{
	freopen("maze.in", "r", stdin);
	scanf("%d%d", &n, &m);
	int u, v;
	for (int i = 1; i <= m; i++)
		scanf("%d%d", &u, &v), adde(u, v);
	memset(mx, -1, sizeof mx);
	memset(my, -1, sizeof my);
}

void outit()
{
	freopen("maze.out", "w", stdout);
	printf("%d", n - ans);
}

int que[maxn], head, tail;

bool bfs()
{
	head = 1, tail = 0;
	bool rt = 0;
	for (int i = 1; i <= n; i++)
		if (mx[i] == -1) que[++tail] = i;
	memset(dx, 0, sizeof dx);
	memset(dy, 0, sizeof dy);
	while (head <= tail)
	{
		int p = que[head++];
		for (int q = h[p]; q; q = nt[q])
		{
			int r = t[q];
			if (!dy[r])
			{
				dy[r] = dx[p] + 1;
				if (my[r] == -1)	rt = 1;
				else
				{
					dx[my[r]] = dy[r] + 1;
					que[++tail] = my[r];
				}
			}
		}
	}
	return rt;
}

bool dfs(int p)
{
	for (int q = h[p]; q; q = nt[q])
	{
		int r = t[q];
		if (dy[r] == dx[p] + 1)
		{
			dy[r] = 0;
			if (my[r] == -1 || dfs(my[r]))
			{
				mx[p] = r; my[r] = p;
				return 1;
			}
		}
	}
	return 0;
}

bool select[maxn];

void got(int i)
{
			for (int q = h[i]; q; q = nt[q])
				if (!select[t[q]])
				{
					++ans;
					mx[i] = t[q]; my[t[q]] = i;
					select[i] = 1, select[t[q]] = 1;
					return ;
				}
}

int main()
{
	init();
	ans = 0;
	memset(select, 0, sizeof select);
	for (int i = 1; i <= n; i++)
		if (!select[i]) got(i);
	while (bfs())
		for (int i = 1; i <= n; i++) if (mx[i] == -1) if (dfs(i)) ++ans;
	outit();
   return 0;
}

你可能感兴趣的:(【Hopcroft Karp】HK算法简介)