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