最小路径覆盖:最小路径覆盖即最小边覆盖,就是找出最小的路径条数,使之成为P的一个路径覆盖.(来源:百度百科),通俗点讲,就是在一个有向图中,找出最少的路径,使得这些路径经过了所有的点。
在一个p*p的有向图中,路径覆盖就是在图中找一些路径,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联。
我们需要知道的是:
1.一个点也是一个路径覆盖,只不过它的路径覆盖长度为0。
2.任何一个顶点有且只有一条路径与之相关联。
路径覆盖与二分图之间的关系(有向无环图):
最小路径覆盖=|p|-最大匹配数(来源百度百科)
其中最大匹配数的求法是把P中的每个顶点pi分成两个顶点pi’与pi’’,如果在p中存在一条pi到pj的边,那么在二分图P’中就有一条连接pi’与pj’‘的无向边;这里pi’ 就是p中pi的出边,pj’‘就是p中pj 的一条入边;
对于公式:最小路径覆盖=|P|-最大匹配数;可以这么来理解;
如果匹配数为零,那么P中不存在有向边,于是显然有:
最小路径覆盖=|P|-最大匹配数=|P|-0=|P|;即P的最小路径覆盖数为|P|;
P’中不在于匹配边时,路径覆盖数为|P|;
如果在P’中增加一条匹配边pi’-->pj’’,那么在图P的路径覆盖中就存在一条由pi连接pj的边,也就是说pi与pj 在一条路径上,于是路径覆盖数就可以减少一个;
如此继续增加匹配边,每增加一条,路径覆盖数就减少一条;直到匹配边不能继续增加时,路径覆盖数也不能再减少了,此时就有了前面的公式;但是这里只是说明了每条匹配边对应于路径覆盖中的一条路径上的一条连接两个点之间的有向边;下面来说明一个路径覆盖中的每条连接两个顶点之间的有向边对应于一条匹配边;
与前面类似,对于路径覆盖中的每条连接两个顶点之间的每条有向边pi—>pj,我们可以在匹配图中对应做一条连接pi’与pj’‘的边, 显然这样做出来图的是一个匹配图(这一点用反证法很容易证明,如果得到的图不是一个匹配图,那么这个图中必定存在这样两条边 pi’—pj’’ 及 pi’ ----pk’’,(j!=k),那么在路径覆盖图中就存在了两条边pi–>pj, pi—>pk ,那边从pi出发的路径就不止一条了,这与路径覆盖图是矛盾的;还有另外一种情况就是存在pi’—pj’’,pk’—pj’’,这种情况也类似可证);
至此,就说明了匹配边与路径覆盖图中连接两顶点之间边的一一对应关系,那么也就说明了前面的公式成立!
大佬博客:https://blog.youkuaiyun.com/qq_41730082/article/details/81456611
例题链接:https://vjudge.z180.cn/problem/HDU-1054
ac代码:
#include <algorithm>
#include <stdio.h>
#include <cstring>
#include <deque>
#include <list>
#include <map>
#include <iostream>
#include <istream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
#define INF 0x3f3f3f3f
#define Max 1000001
int book[401];
int path[401][401];
int match[401];
int cnt;
bool dfs(int x)
{
for(int i=1; i<=cnt; i++)
{
if(!book[i]&&path[x][i])
{
book[i]=1;
if(match[i]==-1||dfs(match[i]))
{
match[i]=x;
return true;
}
}
}
return false;
}
int solve()
{
int ans=0;
memset(match,-1,sizeof(match));
for(int i=1; i<=cnt; i++)
{
memset(book,0,sizeof(book));
if(dfs(i))
{
ans++;
}
}
return ans;
}
int main()
{
int t;
cin>>t;
while(t--)
{
char str[41][11];
int p[41][11];
memset(p,0,sizeof(p));
int h,w;
cin>>h>>w;
cnt=0;
for(int i=0; i<h; i++)
{
getchar();
for(int j=0; j<w; j++)
{
cin>>str[i][j];
if(str[i][j]=='*')
{
p[i][j]=++cnt;
}
}
}
memset(path,0,sizeof(path));
int next[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};
for(int i=0; i<h; i++)
{
for(int j=0; j<w; j++)
{
if(str[i][j]=='*')
{
for(int k=0; k<4; k++)
{
int tx=i+next[k][0];
int ty=j+next[k][1];
if(tx<0||tx>=h||ty<0||ty>=w)
{
continue;
}
if(str[tx][ty]=='*')
{
path[p[i][j]][p[tx][ty]]=1;
}
}
}
}
}
cout<<solve()/2<<endl;
cout<<cnt-solve()/2<<endl;
}
return 0;
}
题目链接:https://vjudge.net/problem/POJ-2594
题解:不是单纯的最小路径覆盖,题目中给出信息==You should notice that the roads of two different robots may contain some same point. ==,你应该注意两个不同机器人的道路可以包含某个相同的点,因为有些点可以重复访问,所以最小路径会有相交,所以还需要用floyd算法求传递闭包。
#include <algorithm>
#include <stdio.h>
#include <stack>
#include <ios>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <set>
using namespace std;
int Map[501][501];
int book[501];
int match[501];
int n,m;
int dfs(int x)
{
for(int i=1;i<=n;i++)
{
if(Map[x][i]&&!book[i])
{
book[i]=1;
if(match[i]==-1||dfs(match[i]))
{
match[i]=x;
return 1;
}
}
}
return 0;
}
int main()
{
while(cin>>n>>m)
{
if(n==0&&m==0)
{
break;
}
memset(match,-1,sizeof(match));
memset(Map,0,sizeof(Map));
int u,v;
while(m--)
{
cin>>u>>v;
Map[u][v]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
if(Map[i][k]&&Map[k][j])
{
Map[i][j]=1;
}
}
}
}
int sum=0;
for(int i=1;i<=n;i++)
{
memset(book,0,sizeof(book));
if(dfs(i))
{
sum++;
}
}
cout<<n-sum<<endl;
}
return 0;
}