最小路径覆盖详解 超级详细(附带例题 Stock Charts(给了题目))

238 阅读2分钟

最小路径覆盖定义:在图G中找出一些路径,每条路径从起点走到终点,使所有点均被覆盖,且只被覆盖一次,选出的这些路径组成路径覆盖。如果找出最少的路径成为一个路径覆盖,则称为最小路径覆盖。

对于不包含圈的有向图,我们可以将其转化为二分图求解

① 转化方法:
将有向图中的每个顶点都拆为 2 个顶点得到,连接左右点对应边
请添加图片描述
② 为何可行:
由于左边连接右边,相当于左边可以到达右边
所以左边剩下的点为各个路径的终点,右边剩下的点为各个路径的起点

③ 最终结果:
| 最小路径数 | = | 顶点数 | - | 最大匹配数 |

④ 为何需要不包含圈(无向图当然也不可以):
如果尝试使用同样的算法,会因为产生圈导致无法正确计算出结果
例如下图就只能得出有 0 条路径,显然不符合题意

请添加图片描述

例题大概意思为有 n 支股票,每个股票有 k 个时间点的数据,将 k 个数据相连,如果两个股票的线段发生重叠,则不能画在一张图中,求最少几张图能全部画下所有股票
输入:
5 2
1 1
2 2
5 4
4 4
4 1
输出:
2

我们如果把可以画在同一张图的两个股票连接在一起,这样便会产生无向图,此时便无法再转化为二分图求解,所以我们得换种思路构造,我们可以将如果 股票i 在 股票j 的上方(下方同理),则将 i 和 j 连接在一起,此时我们便可以构造无环有向图

然后按照上面思路求解即可,代码如下:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;
vector<int> edge[105];
int data[105][35];
bool used[105];
int match[105];
add_edge(int i, int j)
{
    edge[i].push_back(j);
}
bool dfs(int o)
{
    used[o] = true;
    for(int i = 0; i < edge[o].size(); i++)
    {
        int u = edge[o][i], w = match[u];
        if(w == -1 || !used[w] && dfs(w))
        {
            match[u] = o;
            return true;
        }
    }
    return false;
}
int main()
{
    int n, k;
    scanf("%d %d", &n, &k);
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < k; j++)
        {
            scanf("%d", &data[i][j]);
        }
    }
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            if(i == j) continue;
            bool flag = false;
            for(int l = 0; l < k; l++)
            {
                if(data[i][l] <= data[j][l])
                {
                    flag = true;
                    break;
                }
            }
            if(!flag)
            {
                add_edge(i, j);
            }
        }
    }
    int res = 0;
    memset(match, -1, sizeof(match));
    for(int i = 0; i < n; i++)
    {
        memset(used, 0, sizeof(used));
        if(dfs(i))
        {
            res++;
        }
    }
    printf("%d\n", n - res);
}