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; }