二分图的最佳完美匹配(和一些变形…
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; // 寻找增广路
时间复杂度都为: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; // 寻找增广路
相关文章推荐
- 有向图最小生成树——最小树形图(朱…
- 每个点与点之间的瓶颈路
- LA 5713 Qin&nbsp…
- uva 11354 Bond
- 最小生成树的一些性质和理解
- poj 2485 简单的最小生…
- 我的博客今天0岁42天了,我领取了…
- LA 4080 Warfare A…
- hdu 2433 最短路树
- 【快速】排序, T(n) = O(nlgn),O(n^2) S(n) = O(lgn) --- 不稳定
- (C# 基础) 类访问修饰符
- LA 3713 Astronauts
- LA 3211 Now or later
- uva 11324
- LA 5135 Mining Your Own Business
- http://kb.cnblogs.com/page/130970/ -http协议
- poj 2942 LA3523 点-双…
- uva 10047
- STL中优先队列的用法
- 并查集及其路径的压缩