您的位置:首页 > 其它

二分图的最佳完美匹配(和一些变形…

2013-12-14 20:03 246 查看
最佳完美匹配分两种:权值和最大的完美匹配、权值和最小的完美匹配。

时间复杂度都为:O(n^4) ;
1、权值和最大的完美匹配

用的算法是KM(匈牙利算法)。

下面分析一个这个算法的原理:

1、首先为每个点建立一个函数(可行顶标) , 使得对于任意的弧有l(x)+l(y)>=w[x][y] (弧有x、y组成 ,
w[x][y]是该弧的权值) , 假设这样的弧组成的图成为相等子图也就是原图的生成子图 , 如果这个相等子图存在最佳完美匹配 ,
那么这个最佳完美匹配也就是原图的最佳完美匹配。

所以我们要做得就是寻找每个节点的函数(可行顶标)来建成存在最佳完美匹配的相等子图 。

2、构造可行顶标的思路:任意构造一个可行顶标(必须满足l(x)+l(y)>=w[x][y]) , 比如y结点顶标为0 ,
而x结点的顶标为它出发所有边的最大权值

3、构成之后 , 再来求相等子图的最佳完美匹配 , 如果不存在 , 那么就改变可形顶标 , 来使更多的边进入相等子图 。

下面是代码:

#include

#include

#include

#include

#include

#include

using namespace std;

const int MAXN = 1000;

int w[MAXN][MAXN] , n , m; //  记录每条边的权值

int lx[MAXN] , ly[MAXN]; //  记录每个点的顶标

int pre[MAXN] ;  // 记录和y中点匹配的点是哪个点

bool s[MAXN] , t[MAXN] ; //  标记x和y的点是否被访问过

void init()

{

    memset(w , 0
, sizeof(w));

}

bool match(int i)  // 
寻找增广路

{

    s[i] =
true;

    for(int j =
1; j <= n; j++)

   
   
if(lx[i]+lx[j] == w[i][j] && !t[j])

   
    {

   
   
    t[j] =
true;

   
   
    if(!pre[j]
|| match(pre[j]))

   
   
    {

   
   
   
    pre[j] =
i;

   
   
   
    return
true;

   
   
    }

   
    }

    return
false;

}

void update()

{

    int a =
1<<30 , i;

    for(i = 1; i
<= n; i++) 

   
   
if(s[i])  //  如果x中的这个点已被标记

   
   
    for(int j =
1; j <= n; j++)

   
   
   
    if(!t[i])
//  如果这个点在y中没有被标记 , 和前面的判断 , 就可以寻找到飞匹配点。

   
   
   
   
    a = min(a ,
lx[i]+ly[j]-w[i][j]);  //  找到最小的a
, 因为这样才能保证是最佳完美匹配

    for(i = 1; i
<= n; i++)

    {

   
   
if(s[i])  lx[i] -= a; 
//  只有被标记的点 , 才修改其顶标

   
   
if(t[i])  ly[i] += a;

    }

}

void km()

{

    int i ,
j;

    for(i = 1; i
<= n; i++)

    {

   
    pre[i] =
lx[i] = ly[i] =
0;   
//  构造可行顶标

   
    for(j = 1; j
<= n; j++)

   
   
    lx[i] =
max(lx[i] , w[i][j]);

    }

    for(i = 1; i
<= n; i++)

    {

   
    for(; ;
)

   
    {

   
   
    for(j = 1; j
<= n; j++)  s[j] = t[j] = 0; 
//初始化x和y中点的状态

   
   
    if(match(i))
break; // 寻找增广路

   
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: