经过三、四天的奋斗,终于有了一点成果,看了很多书《黑书》,《图论导引》,《图论与代数结构》,《黑书指导》
及网络资料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 ;
}
// 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 ;
}
#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 ;
}