链接
题解
- 首先对有向无环图进行拆点,即将每个点拆为入点$i$和出点$i+n$,得到一张二分图
- 结论:有向无环图的最小路径覆盖等于$n$-拆点二分图最大匹配数
- 简单证明:因为此题要求路径不相交,所以每一个点的入度和出度都不超过1,而每个节点都会被覆盖即每个节点的入度和出度至少有一个为1.所以最小路径覆盖的边$(x,y)$对应拆点二分图中的一个匹配。显然最小路径覆盖数等于最终出度为0的点的个数,而出度为0的点对应拆点二分图左部的未匹配点个数。所以结论成立
- 建完图跑dinic或者匈牙利都行
查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e4 + 5;
const int mod = 1e9 + 7;
ll qpow(ll a, ll b)
{
ll res = 1;
for (; b; b >>= 1)
{
if (b & 1)
res = res * a % mod;
a = a * a % mod;
}
return res;
}
struct graph
{
int head[maxn], nxt[maxn << 1], to[maxn << 1], w[maxn << 1], sz;
void init() { memset(head, -1, sizeof(head)); }
graph() { init(); }
void push(int a, int b, int c) { nxt[sz] = head[a], to[sz] = b, w[sz] = c, head[a] = sz++; }
int &operator[](const int a) { return to[a]; }
} g;
int d[405], now[405];
int s, t;
bool bfs()
{
memset(d, 0, sizeof(d));
queue<int> q;
q.push(s);
d[s] = 1;
now[s] = g.head[s];
while (!q.empty())
{
int x = q.front();
q.pop();
for (int i = g.head[x]; ~i; i = g.nxt[i])
{
if (!d[g[i]] && g.w[i])
{
d[g[i]] = d[x] + 1;
now[g[i]] = g.head[g[i]];
q.push(g[i]);
if (g[i] == t)
return 1;
}
}
}
return 0;
}
int dinic(int x, int flow)
{
if (x == t)
return flow;
int res = flow, i, k;
for (i = now[x]; ~i && res; i = g.nxt[i])
{
if (d[g[i]] == d[x] + 1 && g.w[i])
{
k = dinic(g[i], min(res, g.w[i]));
if (!k)
d[g[i]] = 0;
res -= k;
g.w[i] -= k;
g.w[i ^ 1] += k;
}
}
now[x] = i;
return flow - res;
}
int pre[maxn], son[maxn];
int main()
{
#ifndef ONLINE_JUDGE
freopen("simple.in", "r", stdin);
freopen("simple.out", "w", stdout);
#endif
int n, m;
scanf("%d%d", &n, &m);
s = 0, t = 2 * n + 1;
for (int i = 1; i <= n; ++i)
{
pre[i] = son[i] = i;
g.push(s, i, 1), g.push(i, s, 0);
g.push(i + n, t, 1), g.push(t, i + n, 0);
}
for (int i = 1, a, b; i <= m; ++i)
{
scanf("%d%d", &a, &b);
g.push(a, b + n, 1), g.push(b + n, a, 0);
}
int flow = 0, maxflow = 0;
while (bfs())
{
while (flow = dinic(s, 1e9))
maxflow += flow;
}
for (int i = 1; i <= n; ++i)
{
for (int j = g.head[i]; ~j; j = g.nxt[j])
{
if (g[j] == 0 || g.w[j] == 1)
continue;
pre[g[j] - n] = i, son[i] = g[j] - n;
break;
}
}
vector<int> path;
for (int i = 1; i <= n; ++i)
{
if (son[i] == i)
{
int tmp = son[i];
path.push_back(tmp);
do
{
tmp = pre[tmp];
path.push_back(tmp);
} while (tmp != pre[tmp]);
reverse(path.begin(), path.end());
for (auto j : path)
printf("%d ", j);
printf("\n");
path.clear();
}
}
printf("%d\n", n - maxflow);
return 0;
}