有向无环图(DAG)的最小路径覆盖

参考博客
最小路径覆盖:在一个有向图中,找出最少的路径,使得这些路径经过了所有的点。

最小路径覆盖分为最小不相交路径覆盖最小可相交路径覆盖
最小不相交路径覆盖:每一条路径经过的顶点各不相同。
最小可相交路径覆盖:每一条路径经过的顶点可以相同。
特别的,每个点自己也可以称为是路径覆盖,只不过路径的长度是0。

【DAG的最小不相交路径覆盖】HDU 1151 Air Raid

Problem Description

给你n个城市,m条有向的道路,每个伞兵起始点可以是任何一个城市,伞兵之间不能走相同的城市。问你至少需要多少个伞兵,可以把所有的城市都覆盖到。

思路:

把原图的每个点拆成vx 和 vy两个点,如果有一条有向边A->B,那么就加边Ax−>By。这样就得到了一个二分图。那么最小路径覆盖=原图的结点数-新图的最大匹配数
新图的最大匹配数 = 原图出点或入点没有公共端点的最大边集合

#include<bits/stdc++.h>
using namespace std;
#define nn 130
vector<int> Map[nn];
int n, vis[nn], match[nn];
bool dfs(int u)
{
    for(int i = 0; i < Map[u].size(); i++)
    {
        int to = Map[u][i];
        if(!vis[to])
        {
            vis[to] = 1;
            if(!match[to] || dfs(match[to]))
            {
                match[to] = u;
                return 1;
            }
        }
    }
    return 0;
}
int solve()
{
    memset(match, 0, sizeof(match));
    int ans = 0;
    for(int i = 1; i <= n; i++)//求最大匹配数
    {
        memset(vis, 0, sizeof(vis));
        if(dfs(i)) ans++;
    }
    return ans;
}
void add(int u, int v)
{
    Map[u].push_back(v);
}
int main()
{
    int T, i, m, u, v;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &n, &m);
        for(i = 0; i <= n; i++) Map[i].clear();
        while(m--)
        {
            scanf("%d %d", &u, &v);
            add(u, v);
        }
        printf("%d\n", n - solve());//最小路径覆盖=原图的结点数-原图出点或入点没有公共端点的最大边集合
    }
    return 0;
}

【DAG的最小可相交路径覆盖】POJ 2594 Treasure Exploration

Problem Description

在外星上有n个点需要机器人去探险,有m条单向路径。问至少需要几个机器人才能遍历完所有的点,一个点可以被多个机器人经过(这就是和单纯的最小路径覆盖的区别)。

思路:

先用floyd求出原图的传递闭包,即如果a到b有路径,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题。
至于为什么,只能自己画图理解,不知如何表达出来

#include<cstdio>
#include<cstring>
using namespace std;
#define nn 550
int Map[nn][nn];
int n, vis[nn], match[nn];
bool dfs(int u)
{
    for(int i = 1; i <= n; i++)
    {
        if(!vis[i] && Map[u][i])
        {
            vis[i] = 1;
            if(!match[i] || dfs(match[i]))
            {
                match[i] = u;
                return 1;
            }
        }
    }
    return 0;
}
int solve()
{
    memset(match, 0, sizeof(match));
    int ans = 0;
    for(int i = 1; i <= n; i++)
    {
        memset(vis, 0, sizeof(vis));
        if(dfs(i)) ans++;
    }
    return ans;
}
void Floyd()
{
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= n; j++)
        {
            for(int k = 1; k <= n; k++)
            {
                if(Map[i][k] && Map[k][j])
                {
                    Map[i][j] = 1;
                }
            }
        }
    }
}
int main()
{
    int m, u, v;
    while(~scanf("%d %d", &n, &m) && n+m)
    {
        memset(Map, 0, sizeof(Map));
        while(m--)
        {
            scanf("%d %d", &u, &v);
            Map[u][v] = 1;
        }
        Floyd();//求传递闭包
        printf("%d\n", n - solve());
    }
    return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值