标签:
二分图:原图G的顶点可以分类两个集合X和Y,所有的边关联的两个顶点恰好一个属于集合X,另一个属于集合Y,则称该图为二分图。
二分图匹配:给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,即一个顶点最多只有一条边。则称M是一个匹配。
二分图最大匹配:图中包含边数最多的匹配称为图的最大匹配。
二分图完美匹配:如果所有点都在匹配边上,则称这个最大匹配是完美匹配。
二分图多重匹配:二分图匹配一对一匹配,这里允许集合Y中的一个元素和集合X中的多个元素匹配(一般有最大限制N),但是集合X中的元素只能和集合Y中的多个元素匹配。
二分图最佳匹配:将二分图加权,在图中找到一个总权值最大的匹配。
二分图最小点覆盖: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和其中一个点关联。
二分图最小点覆盖 = 二分图最大匹配
二分图最小边覆盖:选择最少的边,使得能够覆盖图中所有的点。
二分图最小边覆盖 = 无向图被重复计算两次,ans = N - 最大匹配数/2
DAG图的最小路径覆盖:用尽量少的不相交简单路径覆盖有向无环图
(DAG)G的所有顶点,这就是DAG图的最小路径覆盖问题。
最小路径覆盖 = 节点数N-最大匹配数
二分图的最大独立集:在N个点的图G中选出若干个点,使这若干个点两两之间没有边,求点数最大值。
二分图的最大独立集数 = 节点数(n)— 最大匹配数(m)
一般是双向边,则被重复计算了两次,ans = N - 最大匹配数/2
二分图最大匹配——匈牙利算法DFS版:
const int MAXN = 220;
bool Map[MAXN][MAXN];
bool bMask[MAXN];
int NX,NY;
int cx[MAXN],cy[MAXN];
int FindPath(int u)
{
for(int i = 1; i <= NY; ++i)
{
if(Map[u][i] && !bMask[i])
{
bMask[i] = 1;
if(cy[i] == -1 || FindPath(cy[i]))
{
cy[i] = u;
cx[u] = i;
return 1;
}
}
}
return 0;
}
int MaxMatch()
{
int res = 0;
for(int i = 1; i <= NX; ++i)
cx[i] = -1;
for(int i = 1; i <= NY; ++i)
cy[i] = -1;
for(int i = 1; i <= NX; ++i)
{
if(cx[i] == -1)
{
for(int j = 1; j <= NY; ++j)
bMask[j] = 0;
res += FindPath(i);
}
}
return res; //返回最大匹配数
}
二分图多重匹配:
const int MAXN = 33; //最大顶点数
int Map[MAXN][MAXN]; //二分图
bool Mask[MAXN]; //寻找增广路径时的标志数组
int NX,NY,N; //NX左集合顶点数,NY右集合顶点数
int vcy[MAXN]; //vcy[i]表示右集合i顶点匹配到左集合的顶点数目
int cy[MAXN][MAXN]; //cy[i][j]表示与右集合i顶点匹配的第j个元素
int limit[MAXN]; //每个右集合各顶点最多匹配左集合顶点的个数
//也可以为limit,表示最多匹配左集合顶点的共同限制数,如果为待求元素,可二分搜索查找答案
bool FindPath(int u) //寻找增广路径
{
for(int i = 1; i <= 5; ++i)
{
if(Map[u][i] && !Mask[i])
{
Mask[i] = 1;
if(vcy[i] < limit[i]) //vcy[i] < limit
{
cy[i][vcy[i]++] = u;
return true;
}
for(int j = 0; j < vcy[i]; ++j) //j < vcy[i]
{
if(FindPath(cy[i][j]))
{
cy[i][j] = u;
return true;
}
}
}
}
return false;
}
void MulMatch() //求多重匹配
{
int Ans = 0;
memset(vcy,0,sizeof(vcy));
for(int i = 1; i <= N; ++i)
{
memset(Mask,0,sizeof(Mask));
Ans += FindPath(i); //计算右边能匹配点个数
/*
if(!FindPath(i))
return false;
*/
}
//return true;
if(Ans == N)
printf("T-shirts rock!\n");
else
printf("I‘d rather not wear a shirt anyway...\n");
}
二分图最佳匹配——KM算法:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 330;
const int INF = 0xffffff0;
int N,NX,NY;
int link[MAXN],lx[MAXN],ly[MAXN],slack[MAXN];
int visx[MAXN],visy[MAXN]; //标记
int Map[MAXN][MAXN]; //存放权值
//lx[],ly[]顶标; link[]记录匹配值
int FindPath(int u) //回溯寻找最优解
{
visx[u] = 1;
for(int i = 1; i <= NY; ++i)
{
if(visy[i])
continue;
int temp = lx[u] + ly[i] - Map[u][i];
if(temp == 0)
//if(Map[u][i] == lx[u] + ly[i]) //说明是相等子图
{
visy[i] = 1;
if(link[i] == -1 || FindPath(link[i]))
{
link[i] = u;
return 1;
}
}
else if(slack[i] > temp)
slack[i] = temp;
}
return 0;
}
int KM() //求权值最大的最佳匹配
{
memset(ly,0,sizeof(ly));
memset(link,-1,sizeof(link));
for(int i = 1; i <= NX; ++i)
{
lx[i] = -INF; //求最小权匹配则 lx[i] = INF
for(int j = 1; j <= NY; ++j)
if(Map[i][j] > lx[i]) //最小权匹配则更改符号
lx[i] = Map[i][j];
}
for(int i = 1; i <= NX; ++i)
{
for(int j = 1; j <= NY; ++j)
slack[j] = INF;
while(1)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(FindPath(i))
break;
int d = INF;
for(int j = 1; j <= NY; ++j)
if(!visy[j] && d > slack[j])
d = slack[j];
if(d == INF)
return ;
for(int j = 1; j <= NX; ++j)
if(visx[j])
lx[j] -= d; //求最小权值则改为 lx[j] += d;
for(int j = 1; j <= NY; ++j)
if(visy[j])
ly[j] += d; //求最小权值则改为 ly[j] -= d;
else
slack[j] -= d;
}
}
int res = 0;
for(int i = 1; i <= NY; ++i)
if(link[i] > -1)
res += Map[link[i]][i];
return res; //输出最佳匹配的最大权值和
}
int main()
{
int N;
while(~scanf("%d",&N))
{
NX = NY = N;
for(int i = 1; i <= N; ++i)
for(int j = 1; j <= N; ++j)
scanf("%d",&Map[i][j]);
printf("%d\n",KM());
}
return 0;
}
标签:
原文地址:http://blog.csdn.net/lianai911/article/details/45506473