题目和上一个很像,还是 n 个点 m 条边,是个有向图,无环,求最小路径覆盖,但是问题来了,这道题可以重复走之前走过的路。
那么对于这个图片来说,可重复最小路径覆盖就是 2 了
有了上一个题的经验我们现在开始想象如何解决。。
问题在于 我们走过之后 都会对每个走过的点设置 link 标记,而每个 link 标记都是标记这下一个相连的点。这么看上去这条路完全就有一些并查集的感觉,一个点连着一个点,那么既然它那么的像并查集, 并查集是可以路径压缩的,而且这样压缩就可以越过它的直接连接的那一个点,而指向父节点的父节点,但是很明显。。我们不需要路径压缩,借助这种思路。我们发现在求最短路那里还有一种神奇的算法 Floyd ! 这种算法虽然有些蠢,但是它有一种神奇的特性, 思路也很明确 a-> b , b->c,那么 a - > c ,借助这种性质,我们在跑匈牙利之前 先 floyd 一下,这样 假设以图为例。
1 - 3 - 5 这样是一条路, 而 2 开始跑的时候 会发现 2 和 4、5都是连着的,但是 5明显不可以了,就指向 4 这样就实现了重复的走了。
话不多说,以下是 AC 代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 505;
int ed[maxn][maxn];
int vis[maxn];
int link[maxn];
int n,m;
void floyd()
{
for(int k=1; k<=n; k++)
for(int j=1; j<=n; j++)
for(int i=1; i<=n; i++)
{
if(ed[i][k] && ed[k][j])
ed[i][j] = 1;
}
}
bool dfs(int u)
{
for(int i=1; i<=n; i++)
{
if(!vis[i] && ed[u][i])
{
vis[i] = 1;
if(link[i] == -1 || dfs(link[i]))
{
link[i] = u;
return true;
}
}
}
return false;
}
int hungry()
{
int sum = 0;
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
if(dfs(i))
sum ++;
}
return sum;
}
int main()
{
while(~scanf("%d%d",&n,&m)&&(n+m))
{
memset(ed,0,sizeof(ed));
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
ed[a][b] = 1;
}
floyd();
int num = 0;
memset(link,-1,sizeof(link));
num = hungry();
printf("%d\n",n - num);
}
return 0;
}