在一个N*N的有向图中,路径覆盖就是在图中找一些路经,使之覆盖了图中的所有顶点,
且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,
那么恰好可以经过图中的每个顶点一次且仅一次);如果不考虑图中存在回路,那么每每条路径就是一个弱连通子集.
由上面可以得出:
1.一个单独的顶点是一条路径;
2.如果存在一路径p1,p2,......pk,其中p1 为起点,pk为终点,那么在覆盖图中,顶点p1,p2,......pk不再与其它的
且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,
那么恰好可以经过图中的每个顶点一次且仅一次);如果不考虑图中存在回路,那么每每条路径就是一个弱连通子集.
由上面可以得出:
1.一个单独的顶点是一条路径;
2.如果存在一路径p1,p2,......pk,其中p1 为起点,pk为终点,那么在覆盖图中,顶点p1,p2,......pk不再与其它的
顶点之间存在有向边.
路径覆盖与二分图匹配的关系:最小路径覆盖=|G|【边数】-最大匹配数;
/*
C++
288KB
0MS
*/
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<vector>
using namespace std;
const int MAXN = 505;
vector<int>map[MAXN];
//邻接矩阵,map[i][j]为1表示Xi和Yj有边相连
int link[MAXN], vis[MAXN];
//link[i]表示最大匹配中与Xi匹配的Y顶点,vis[i]表示i是否被访问过
//邻接表比邻接矩阵时间复杂度要低。
int n;
bool find(int x)
{
for (int i = 0; i < map[x].size(); i++)//枚举与x相关联的其他点
{
int u = map[x][i];
if (!vis[u]) //u与x邻接,且没有访问过
{
vis[u] = 1;//标记已访问
if (link[u] == -1 || find(link[u]))
{//如果v没有匹配,或者v已经匹配了,但从link[u]出发可以找到增广路
link[u] = x;//把x匹配给u
return true;
}
}
}
return false;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int m,u,v,ans=0;
scanf("%d", &n);//顶点数
scanf("%d", &m);//边数
memset(link, -1, sizeof(link));
for (int i = 0; i < m; i++)
{
scanf("%d%d", &u, &v);
map[u].push_back(v);
}
for (int i = 0; i < n; i++)
{
memset(vis, 0, sizeof(vis));
if (find(i))//每找到一条增广路,可使得匹配数加1
{
ans++;
}
}
printf("%d\n", (n - ans));
for (int i = 0; i < n; i++)//清除数组map的信息
{
map[i].clear();
}
}
return 0;
}