Hopcroft–Karp algorithm 总结

经过三、四天的奋斗,终于有了一点成果,看了很多书《黑书》,《图论导引》,《图论与代数结构》,《黑书指导》

及网络资料http://en.wikipedia.org/wiki/Hopcroft-Karp_algorithm

http://hi.baidu.com/czyuan_acm/blog/item/cd482e35d3947e1890ef3919.html

http://hi.baidu.com/xiaotiandm/blog/item/1fe492dbb717c96ad1164e42.html等。

也感谢这些博主,及给我启发的大牛们。

作为Hungarian算法的优化,在解决大规模的二分匹配问题时,也确实堪称一把利刃,再加上贪心初始化,就更没问题了。

实现方法确实有很多,效率也不尽相同,大牛说浙大的模板很快,但是还没看懂,最后选择了wiki上提供的方法。上面只有

伪代码,事实证明完全按照这个写行不通,参考某仁兄的代码,终于完成了,速度还行,也比较精简。

至于理论部分,由于没有完全贯通,就先不写了,附上两道题:

 

HDU 2389:

代码
//625MS
//546MS + 贪心
#include <stdio.h>
#include
<string.h>
#include
<math.h>
#define NL 3010
#define EP 1e-10

int t, m, n;
int ck[NL][3];
int arm[NL][2];
int adj[NL][NL];
int mx[NL], my[NL], dx[NL], dy[NL];
int que[NL];

inline
double dis(int x, int y)
{
return sqrt((ck[x][0]-arm[y][0])*(ck[x][0]-arm[y][0]) + (ck[x][1]-arm[y][1])*(ck[x][1]-arm[y][1])+0.0);
}

void init()
{
int i, j;
memset(adj,
0, sizeof(adj));
for (i=1; i<=m; i++) {
for (j=1; j<=n; j++) {
double d = dis(i, j);
if (d/ck[i][2] < t+EP) {
adj[i][
++adj[i][0]] = j;
}
}
}
}

//先广搜,遍历出层次标号
//初始时将X集合中未匹配点入队,在生产匈牙利树的过程(即广搜),对于未匹配的Y集合中的点,即找到一条dis[v] = dis[u]+1;
//对于已匹配点,dis[my[v]] = dis[v]+1,并入队,继续迭代。
bool BFS()
{
int front, rear;
int i;
front
= rear = 0;
for (i=1; i<=m; i++) {
if (mx[i] == -1) {
que[rear
++] = i;
}
}
for (i=0; i<=m; i++) dx[i] = 0;
for (i=0; i<=n; i++) dy[i] = 0;
bool flg = false;
while (front < rear) {
int u = que[front++];
for (i=1; i<=adj[u][0]; i++) {
int v = adj[u][i];
if (dy[v] == 0) {
dy[v]
= dx[u] + 1;
if (my[v] == -1) {
flg
= true;
}
else {
dx[my[v]]
= dy[v] + 1;
que[rear
++] = my[v];
}
}
}
}
return flg;
}

void greed(int &ans)
{
int i, j;
for (i=1; i<=m; i++) {
for (j=1; j<=adj[i][0]; j++) {
int t = adj[i][j];
if (my[t] == -1) {
mx[i]
= t;
my[t]
= i;
ans
++;
break;
}
}
}
}
//DFS在层次标号的引导下遍历,类似匈牙利算法
bool DFS(int u)
{
for (int v=1; v<=adj[u][0]; v++) {
int t = adj[u][v];
if (dy[t] == dx[u]+1) {
dy[t]
= 0;
if (my[t] == -1 || DFS(my[t])) {
my[t]
= u;
mx[u]
= t;
return true;
}
}
}
return false;
}

int main()
{
int T, cs=1;
int i;
scanf(
"%d", &T);
while (T--) {
scanf(
"%d%d", &t, &m);
for (i=1; i<=m; i++) scanf("%d%d%d", &ck[i][0], &ck[i][1], &ck[i][2]);
scanf(
"%d", &n);
for (i=1; i<=n; i++) scanf("%d%d", &arm[i][0], &arm[i][1]);
init();
for (i=0; i<=m; i++) mx[i] = -1;
for (i=0; i<=n; i++) my[i] = -1;

int ans = 0;
greed(ans);
while (BFS()) {
//每次寻找极大匹配集
for (i=1; i<=m; i++) {
if (mx[i] == -1 && DFS(i)) ans++;
}
}
printf(
"Scenario #%d:\n", cs++);
printf(
"%d\n\n", ans);
}
return 0;
}

SPOJ MATCHING https://www.spoj.pl/problems/MATCHING/

数据规模比HDU 2389更大

代码
//1550ms
#include <stdio.h>
#include
<string.h>
#define CAP 50010

int n, m;
int mx[CAP], my[CAP], dis[CAP], que[CAP];
bool used[CAP];
struct Node {
int id;
struct Node *next;
}adj[CAP];

bool BFS()
{
int front, rear;
int i, j;
front
= rear = 0;
for (i=1; i<=n; i++) {
if (mx[i] < 0) {
dis[i]
= 0;
que[rear
++] = i;
used[i]
= true;
}
else {
used[i]
= false;
}
}
bool suc = false;
while (front < rear) {
int u = que[front++];
struct Node *p = &(adj[u]);
while (p->next) {
int v = p->next->id;
if (my[v] < 0) suc = true;
else if (!used[my[v]]) {
dis[my[v]]
= dis[u]+1;
used[my[v]]
= true;
que[rear
++] = my[v];
}
p
= p->next;
}
}
return suc;
}

bool DFS(int u)
{
struct Node *p = &(adj[u]);
while (p->next) {
int v = p->next->id;
if (my[v] < 0
|| dis[my[v]] == dis[u]+1 && DFS(my[v])) {
my[v]
= u;
mx[u]
= v;
dis[u]
= -1;
return true;
}
p
= p->next;
}
return false;
}

int main()
{
int i, j, P;
int a, b;
struct Node *p;
while (scanf("%d%d%d", &n, &m, &P) != EOF) {
for (i=1; i<=n; i++)
adj[i].next
= NULL;
for (i=0; i<P; i++) {
scanf(
"%d%d", &a, &b);
p
= new Node;
p
->id = b;
p
->next = adj[a].next;
adj[a].next
= p;
}
memset(mx,
-1, sizeof(mx));
memset(my,
-1, sizeof(my));
int match = 0;
while (BFS()) {
for (i=1; i<=n; i++) {
if (mx[i] < 0 && DFS(i))
match
++;
}
}
printf(
"%d\n", match);
}
return 0;
}

 

posted @ 2010-06-09 19:00  superbin  阅读(2488)  评论(0编辑  收藏  举报