题意:不是赤裸裸的最小路径覆盖(走遍所有的点),正常的最小路径覆盖中两个人走的路径不能有重复的点,而本题可以重复。
分析:我们仍可将问题转化为最小路径覆盖。如果一个人需要经过另一个人走过的点的时候,让他直接从该点上空飞过去,越过该点,直接走下一个点。如果我们赋予每个人这种能力,那么求得的无重复点的最小路径覆盖结果,就是题目要求的结果,因为需要重复的地方只要飞过去,就可以不重复了。赋予这个能力的方法就是把所有点能间接到达的点全都改为直接到达。然后正常求最小路径覆盖即可。
#include <iostream>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX=510;
int map[MAX][MAX]; //图
int vis[MAX]; //访问标志
int match[MAX]; //匹配数组
int N,M; //N:顶点数 M:边数
bool Dfs(int u) //判断顶点u是否可以找到可匹配的顶点
{
for (int v=1;v<=N;v++)
{
if (!vis[v] && map[u][v])
{
vis[v]=1;
if (match[v]==0 || Dfs(match[v]))
{
match[v]=u;
return true;
}
}
}
return false;
}
void Floyd() //传递闭包:建新图
{
for (int k=1;k<=N;k++)
{
for (int i=1;i<=N;i++)
{
for (int j=1;j<=N;j++)
{
if (map[i][k] && map[k][j]) //顶点i和顶点j可到达
{
map[i][j]=1;
}
}
}
}
}
int main()
{
int i;
int x,y;
while (scanf("%d%d",&N,&M) && (N | M))
{
memset(map,0,sizeof(map));
memset(match,0,sizeof(match));
//输入边
for (i=1;i<=M;i++)
{
scanf("%d%d",&x,&y);
map[x][y]=1;
}
//建新图
Floyd();
//求最大匹配
int sum=0;
for (i=1;i<=N;i++)
{
memset(vis,0,sizeof(vis));
if (Dfs(i))
{
sum++;
}
}
printf("%d\n",N-sum); //输出最小路径覆盖
}
return 0;
}