http://poj.org/problem?id=2594
题意:
派机器人去火星寻宝,给出一个无环的有向图,机器人可以降落在任何一个点上,再沿着路去其他点探索,我们的任务是计算至少派多少机器人就可以访问到所有的点。不同的机器人可以经过同一个点。
显然是最小路径覆盖问题,一般的最小路径覆盖的话,路径没没有重叠的,也就是每个点只经过一次,对于这种可以路径重叠的情况,我们需要做一下闭包传递,也就是
如果 1》》3》》5
并且2》》3》》4
这样的话,因为2连通了1,5,联通了,2,4,如果按原图走,我们会得到最小路径为3的结果,但实际 按135 234走两次就够了,因此,我们可以把1-5,2-4直接连起来,和flody类似,三个for,把间接连通的点都直接连通起来
因此这样就相当于可以走重复路径了
然后再按一般的求最小路径覆盖算法即可得到正确答案。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const double pi=acos(-1.0);
double eps=0.000001;
//顶点编号从0开始的
const int MAXN = 510;
int uN,vN;//u,v的数目,使用前面必须赋值
int g[MAXN][MAXN];//邻接矩阵
int linker[MAXN];
bool used[MAXN];
bool dfs(int u)
{
for(int v = 0; v < vN;v++)
if(g[u][v] && !used[v])
{
used[v] = true;
if(linker[v] == -1 || dfs(linker[v]))
{
linker[v] = u;
return true;
}
}
return false;
}
int hungary()
{
int res = 0;
memset(linker,-1,sizeof(linker));
for(int u = 0;u < uN;u++)
{
memset(used,false,sizeof(used));
if(dfs(u))res++;
}
return res;
}
int t1[505],t2[505];
int main()
{
int n,m;
int i,j,x,k,y;
while(scanf("%d%d",&n,&m)!=EOF)
{
if (!n&&!m) break;
memset(g,0,sizeof(g));
for (i=0;i<m;i++)
{
scanf("%d %d",&x,&y);
g[x-1][y-1]=1;
}
for (k=0;k<n;k++)
for (i=0;i<n;i++)
for (j=0;j<n;j++)
g[i][j] = g[i][j]||(g[i][k]&&g[k][j]);
uN=vN=n;
int ret=hungary();
//最小路径覆盖=最大独立集=n-最大匹配数
printf("%d\n",n-ret );
}
return 0;
}