概念
匹配:一个边的集合,任何两边都没有公共点。
最大匹配:匹配边数最多的匹配。
完美匹配:所有顶点都是匹配点。
最小点覆盖:用最少的点,让每条边都至少和其中一个点相关联。
最小边覆盖:用尽量少的不相交简单路径覆盖有向无环图G的所有顶点。
最大独立集:在n个点的图G中选出两两之间没有边的尽量多的m个点,这m个点所组成的集合。
关系
最小点覆盖=最大匹配数
最小边覆盖=顶点数-最小顶点覆盖(最大匹配)
顶点数=二分图中最大独立集+最小顶点覆盖(最大匹配)
最大独立集的顶点数量=所有顶点数-最小点覆盖
最大团中顶点数量 = 补图的最大独立集中顶点数量
例题
出处:http://blog.csdn.net/u012294939/article/details/43763005
(时间有限不知道这些题以后会不会做,在这先写上一句话题解)
HDU 1083&&POJ 1469
一道Hungary的裸题
这道题必须要用邻接矩阵,否则要MLE。
注意:
1、Hungary(mch[i])
2、vis[i]=1; 单指一边,不能写在for的外面
//MLE
#include<cstdio>
#include<cstring>
using namespace std;
//int to[M],nxt[M],head[PO],etot;
bool vis[305],ed[105][305];int mch[305];
int p,n;
bool Hungary(int u)
{
//for(int i=head[u];i;i=nxt[i])
for(int i=1;i<=n;i++)
if(ed[u][i]){
if(vis[i]) continue;
vis[i]=1;
if(!mch[i]||Hungary(mch[i])){
mch[i]=u;
return 1;
}
}
return 0;
}
void init()
{
memset(mch,0,sizeof(mch));
memset(ed,0,sizeof(ed));
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%d%d",&p,&n);
for(int i=1;i<=p;i++){//课程
int k;
scanf("%d",&k);
for(int j=1;j<=k;j++){
int stu;
scanf("%d",&stu);
ed[i][stu]=1;
}
}
bool MK=0;
for(int i=1;i<=p;i++){
memset(vis,0,sizeof(vis));
if(!Hungary(i)){
MK=1;break;
}
}
if(!MK) printf("YES\n");
else printf("NO\n");
}
return 0;
}
HDU 1151/POJ1422 DAG的最小不相交路径覆盖
翻了好多篇博客就看懂了这篇:
http://www.cnblogs.com/icode-girl/p/5418461.html
用自己的语言简述一下:若一个点不是结尾节点,那么它在二分图中一定还连向了别的节点(也就是它和那个点形成了一个匹配)。在二分图中求最大匹配也就是求有多少个点不是结尾节点。
所以总点数-最大匹配数=有多少个点是结尾节点
所以可得那个更简短的公式:
DAG的最小路径覆盖数=DAG图中的节点数-相应二分图中的最大匹配数.
POJ 2594 DAG最小的可相交路径覆盖
解法:先用floyd若u可以到达v则在二分图上连接u->v。接下来的步骤就和上一题一样了(求最大匹配数)。
翻了很多篇博客都没有对于解法的解释证明。
在这里借用上一题的证明感性理解一下:
最大匹配数也就是看多少个节点不是结尾节点。在这里DAG中一个节点(u)能到达另一节点(v),那二分图上一定有边使它们(u->v)相连。因为没说一个节点只能被一条路径经过,所以u在与v相连后在DAG中就不会受限制了。(限制指:在DAG中u->u’直接相连但u’已匹配别的点因此u和u’不能匹配,因此u变成结尾节点。而这题中新的建二分图方式就可使u与v相匹配,而u就不会变成结尾节点了)。
HDU 1150&&POJ 1325 最小顶点覆盖
POJ 2446
做了这道题但是有个地方还没想通,先不放题解了
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 50;
const int NN = 2024 + 10;
const int M = 200000 + 10;
int n,m,k;
int a[N][N],head[NN],to[M],nxt[M],etot;
bool vis[NN];int match[NN];
int p(int i,int j){
return (i-1)*n+j;
}
void adde(int u,int v)
{
to[++etot]=v;
nxt[etot]=head[u];
head[u]=etot;
}
bool hungary(int u)
{
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(vis[v]) continue;
vis[v]=1;
if(!match[v]||hungary(match[v])){
match[v]=u;return 1;
}
}
return 0;
}
void init()
{
etot=0;
memset(head,0,sizeof(head));
memset(a,0,sizeof(a));
memset(match,0,sizeof(match));
}
int main()
{
while(scanf("%d%d%d",&m,&n,&k)!=EOF){
init();
for(int i=1;i<=k;i++){
int x,y;
scanf("%d%d",&x,&y);//[y][x]
a[y][x]=1;
}
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
if(!a[i][j]){
if(!a[i-1][j]&&i-1>=1){
if((i+j)%2) adde(p(i,j),p(i-1,j));
else adde(p(i-1,j),p(i,j));
}
if(!a[i][j-1]&&j-1>=1){
if((i+j)%2) adde(p(i,j),p(i,j-1));
else adde(p(i,j-1),p(i,j));
}
}
int mch=0;
for(int i=1;i<=n*m;i++)
if(head[i]){
memset(vis,0,sizeof(vis));
if(hungary(i)) mch++;
}
if(mch*2==n*m-k) printf("YES");
else printf("NO");
}
return 0;
}
HDU 2819
http://www.cnblogs.com/gj-Acit/archive/2013/08/17/3265502.html
感觉这篇证明构建二分图的超清晰!!
不过性质的证明我看了那么多题解还是没有懂……