1.前置知识
1.最小路径点覆盖
DAG中选出最小数量的不相交路径(无公共点),将所有点覆盖。
求法:将DAG中的点拆成出点和入点,构成一个二分图。
则原图的最小路径点覆盖转化到新图中:
1.无公共点->每个点最多只有一个出度一个入度 ->…->新图中的任意两条边之间不相交-> 新图中的边都是匹配边。
小结论:每个路径终点 对应 一个左侧非匹配点
<=> 让左侧非匹配点最少 n-m
<=> 让左侧匹配点最多 m
<=> 找最大匹配边数 m
->二分图最大匹配数 = 总点数-最小路径点覆盖
2.最小路径重复点覆盖
相较于最小路径点覆盖,取消了无公共点的限制
求法:先做一次传递闭包。
原图G最小路径重复点覆盖==新图G’最小路径点覆盖。
(证明没怎么看懂 ,wsfw)
2.题意分析
求DAG中 选择尽可能多的点,使两点之间不能相互到达。
等价于求最小路径重复点覆盖。
最小路径重复点覆盖中的每一条路径,都只能选择一个点。
最优解也就是每条路径都要选一个点。
3.代码
#include <bits/stdc++.h>
using namespace std;
//-----pre_def----
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define fir(i, a, b) for (int i = (a); i <= (b); i++)
#define rif(i, a, b) for (int i = (a); i >= (b); i--)
#define endl '\n'
#define init_h memset(h, -1, sizeof h), idx = 0;
#define lowbit(x) x &(-x)
//---------------
const int N = 200 + 10, M = 30010;
int n, m;
bool st[N], d[N][N];
int match[N];
bool find(int u) //匈牙利
{
for (int i = 1; i <= n; i++)
{
if (d[i][u] && !st[i])
{
st[i] = true;
int t = match[i];
if (t == 0 || find(t))
{
match[i] = u;
return true;
}
}
}
return false;
}
void init() {}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
int StartTime = clock();
#endif
scanf("%d%d", &n, &m);
while (m--)
{
int a, b;
scanf("%d%d", &a, &b);
d[a][b] = true;
}
//传递闭包
fir(k, 1, n)
fir(i, 1, n)
fir(j, 1, n)
d[i][j] |= d[i][k] & d[k][j];
//求最大匹配
int res = 0;
fir(i, 1, n)
{
memset(st, 0, sizeof st);
res += find(i);
}
printf("%d\n", n - res);
#ifndef ONLINE_JUDGE
printf("Run_Time = %d ms\n", clock() - StartTime);
#endif
return 0;
}
再战图论