最小路径覆盖定义:在图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);
}