前言:
之前已经介绍了最小点覆蓋与最大点独立,那么接下来就应该是最小边覆蓋问题了。最小边覆蓋问题有很多变种,其中最常见的就是DAG上的最小路径覆蓋,这种问题可以转化成二分图最大匹配解决。
基本定理:
根据博客(一),有定理如下:
定理4:二分图中,无孤立点,点独立数=边覆蓋数=N-边独立数
若存在M个孤立点,上述定理3、4在顶点数N扣除M后成立。
定理7:无向图中,无孤立点,
1)若M为G的一个最大匹配,对于G中M的每个未覆蓋点v,选取一条与v相关联的边所组成的边的集合为N,则W=M∪N为G的最小边覆蓋;
2)若W为G的一个最小边覆蓋,若W中存在相邻的边就移去一条,设移去的边集为N,则M=W-N为G中一个最大匹配。
算法思路:
最小边覆蓋:
根据定理4和7,我们可以得到求解最小边覆蓋的算法。最小边覆蓋数可以通过求解二分图最大匹配知道,在求得一种匹配方案之后,我们需要找到边覆蓋方案,根据定理7,我们知道对于已匹配的点,我们可以直接使用匹配边;对于未匹配点,随便挑选一条相连边。
DAG最小路径覆蓋:
这种问题是最小边覆蓋的一个变种,解决问题的时候需要将原图的一个点拆成两个点,如果两个结点X和Y存在边,则将左图的X与右图的Y连边。之后求解二分图最大匹配,最终的最小路径覆蓋数=结点数-最大匹配数,可以解决。
例题解析:
1、最小边覆蓋:
题目链接:URAL 1109
解题思路:
最基本的最小边覆蓋,直接使用匈牙利算法求匹配数再计算边覆蓋数即可。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,K,pre[1005];
bool a[1005][1005],vis[1005];
bool dfs(int index)
{
for(int i=1;i<=m;i++)
{
if(!vis[i]&&a[index][i])
{
vis[i]=1;
if(!pre[i]||dfs(pre[i]))
{
pre[i]=index;
return 1;
}
}
}
return 0;
}
int hungary()
{
int cnt=0;
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
if(dfs(i))
cnt++;
}
return cnt;
}
int main()
{
while(~scanf("%d %d %d",&n,&m,&K))
{
memset(a,0,sizeof(a));
memset(pre,0,sizeof(pre));
int x,y;
for(int i=0;i<K;i++)
{
scanf("%d %d",&x,&y);
a[x][y]=1;
}
printf("%d\n",n+m-hungary());
}
return 0;
}
2、DAG最小路径覆蓋:
题目链接:UVALIVE 3126
解题思路:
该题目的描述就是一个很裸的DAG最小路径覆蓋的问题描述,直接按照前面所说的算法思路可以解决。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define maxn 505
using namespace std;
int startx[maxn],starty[maxn],endx[maxn],endy[maxn];
int n,vy[maxn],prey[maxn],a[maxn][maxn],t[maxn];
int dist(int x1,int y1,int x2,int y2){
return abs(x1-x2)+abs(y1-y2);
}
bool dfs(int index)
{
for(int i=1;i<=n;i++)
{
if(!vy[i]&&a[index][i])
{
vy[i]=1;
if(!prey[i]||dfs(prey[i]))
{
prey[i]=index;
return 1;
}
}
}
return 0;
}
int hungary()
{
int ans=0;
memset(prey,0,sizeof(prey));
for(int i=1;i<=n;i++)
{
memset(vy,0,sizeof(vy));
if(dfs(i))
ans++;
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int h,m;
for(int i=1;i<=n;i++)
{
scanf("%02d:%02d %d %d %d %d",&h,&m,&startx[i],&starty[i],&endx[i],&endy[i]);
t[i]=h*60+m;
}
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++)
{
int d=dist(startx[i],starty[i],endx[i],endy[i]);
for(int j=1;j<=n;j++)
if(t[i]<t[j] && t[j]-t[i]-1 >= d+dist(endx[i],endy[i], startx[j],starty[j]))
a[i][j]=1;
}
int ans=hungary();
printf("%d\n",n-ans);
}
return 0;
}