/*
3 最小点覆盖、最大独立集、最小路径点覆盖、最小路径重复点覆盖
最大匹配数 = 最小点覆盖 = 总点数-最大独立集 = 总点数-最小路径覆盖
最小路径覆盖
DAG(有向无环图)
用最少的互不相交的路径 将所有点覆盖
1
2
...
n
拆成两个点
1 1'
2 2'
... ...
n n'
1→2→3
1 1'
↘
2 2'
↘
3 3'
... ...
n n'
出点 入点
原图有边i→j
则有 i→j'
出点 入点
原图变为从左边(出点)连向右边(入点)的二分图
则最少互不相交的路径=n-m(最大点覆盖数量)
原图中的每条路径 转化到新图中
每个点最多只有一个出度一个入度
<=> 新图中的任意两条边之间不相交
<=> 新图中的边都是匹配边
每个路径终点 对应 一个左侧非匹配点(3作为终点 在新图中没出边)
<=> 让左侧非匹配点最少 n-m
<=> 让左侧匹配点最多 m
<=> 找最大匹配边数 m
*/
/*
扩展:取消对路径互不相交的约束后
问题-最小路径重复点覆盖
1 先求传递闭包
<=>
o→o→o => o→o→o
↘↗
G G'
2 原图G最小路径重复点覆盖==新图G'最小路径覆盖
证:
原图G中多条路径点重复(例如第一条路径和第二条路径都经过A) 把重复点跳过
→
↑ ↓
o→o→o→o→o→o
↓ A ↑ A
→
对第3到第n条边
如果当前边中点在前面出现过 则跳过
做完后 原图G转化为新图G'
新图G'转化为原图G(直接把外跳边取消)
→
↑ ↓
o→o→o→o→o→o
↓ A ↑ A
→
*/
/*
本题
最小路径cnt条重复点覆盖
答案==cnt
则需证≤cnt
和≥cnt
证≤cnt
由于cnt条路径把所有点全覆盖了
1 所以选k个点从这cnt条路径上选
2 每条路径上不能选≥2个点 最多只能选1个点
否则一条路径上前面的点一定能看到后面的点(矛盾)
3 则答案必然≤cnt(最多cnt条路线,每条路线最多选1个,最多选cnt个点)
证≥cnt
构造:
cnt条路线的cnt个终点放到集合E中
找一下这个E里面 每一个终点 所有可以到的点
next(E) : 从E中每个点出发可以到达所有点的集合
1. 如果E和next(E)无交集 说明E内的点两两之间不能互相到达 说明E中所有点就是一种方案(k=cnt)
2. 如果E和next(E)有交集
我们让终点E[i]沿有向边倒着走,走到E[i]不属于next(E)为止,直到做到E和next(E)没有交集为止
最多走到起点能达到E和next(E)没有交集
反证:
假设一直往前退到起点都不能保证E[i]不属于next(E)
则说明E[i]所在的起点(其他点都能被间接到达)都是E中所有终点(其他路径)能到达的
那这条路径就没有存在的价值了
可以找到能到达E[i]的路径path,把当前E[i]接到path后
E[i]作为起点的路径上的所有点都可以被覆盖,总路径数cnt-1
这就和cnt是最小路径重复点覆盖矛盾
所以答案就是cnt
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 210, M = 30010;
int n, m;
bool d[N][N], st[N];
int match[N];
bool find(int x)
{
for(int i = 1;i<=n;i++)
{
if(d[x][i] && !st[i])//x能到i 且i没被用
{
st[i] = true;
int t = match[i];
if(t==0 || find(t))
{
match[i] =x;
return true;
}
}
}
return false;
}
int main()
{
cin >> n >> m;
while(m--)
{
int a,b;
cin >> a >> b;
d[a][b] = true;
}
//传递闭包
// i k j i k j
// o→o→o => o→o→o
// ↘↗
// G G'
for(int k = 1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j = 1;j<=n;j++)
{
d[i][j] |=d[i][k] & d[k][j];
}
}
}
int res = 0;
for(int i = 1;i<=n;i++)
{
memset(st,0,sizeof st);
if(find(i))res++;
}
cout << n-res << endl;
return 0;
}
我在想,1-2 2-3 4-2的话,2不是就有2个入度的吗
%%%%%