POJ2594 - Treasure Exploration

原题链接

Description

给出一个有向无环图(|V|500,|E|5000),求最少用多少条路径才能覆盖所有点,路径可以相交。

Solution

首先考虑若路径不能相交如何实现:
很明显n条路径一定可以覆盖,即每个顶点上都有一个自己到自己的路径。然后我们尝试合并这n条路径。

把原来的有向图转换成一个二分图,每条边(u,v)转化为二分图中左边的u和右边的v的边。可以看到,二分图中的一个匹配就相当于合并了两条路径,覆盖所需的路径数就可以-1。所以答案就是n最大匹配数。
然后对于路径可以相交的本题,可以先跑一遍Floyd算出u是否能到v,然后解法如上。正确性简单来说就是可以忽略原路径上的点只能用一次的限制,例如1-2-5和3-5-4的两条路径,现在3-5-4可以越过5而视为3-4。

时间复杂度为O(n3)

Code

//Treasure Exploration
#include <cstdio>
#include <cstring>
int const N=500+10;
int n,m; bool ed[N][N];
void Floyd()
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                ed[i][j]|=ed[i][k]&ed[k][j];
}
int link[N]; bool used[N];
int find(int u)
{
    for(int v=1;v<=n;v++)
    {
        if(!ed[u][v]||used[v]) continue;
        used[v]=true;
        if(!link[v]||find(link[v])) {link[v]=u; return true;}
    }
    return false;
}
int match()
{
    int res=0; memset(link,0,sizeof link);
    for(int i=1;i<=n;i++)
    {
        memset(used,0,sizeof used);
        if(find(i)) res++;
    }
    return res;
}
int main()
{
    while(true)
    {

    scanf("%d%d",&n,&m); if(n==0&&m==0) break;
    memset(ed,0,sizeof ed);
    for(int i=1;i<=m;i++)
    {
        int u,v; scanf("%d%d",&u,&v);
        ed[u][v]=true;
    }
    Floyd();
    printf("%d\n",n-match());

    }
    return 0;
}

P.S.

多组测试数据,别忘了清数组啊。

posted @ 2017-12-27 11:37  VisJiao  阅读(115)  评论(0编辑  收藏  举报