题意:
PS:这题的题面简直有毒
定义一个图的简单路:
- 一个结点是一条简单路;
- 从一个结点出发不分叉地不重复地沿着边经过若干个点构成一条简单路
- 定义不是很严谨,不过应该能看懂啥意思
- 题面不是给正常人看的
问给定一个(n)个点的有向无环图(DAG),最少用几条简单路能把这个图覆盖过来?
思路:
注意这是一个有向图!有向图!有向图!因为习惯性地考虑无向图,导致我想了半天也没有想到网络流解QAQ。
首先我们考虑,如果我们把(n)个点都分别看成一条简单路,那么我们可以用(n)条简单路把这个图覆盖过来;
然后我们考虑,我们每次把不在同一条简单路中的两个点(的简单路)合并成一条简单路,简单路就会减少一条;
然后这是一个有向图;
然后我们就可以拆点了。
注意,因为是简单路,所以每个点最多只能一进一出,所以我们按照如下规则建图:
对于每个点(i),我们拆成((x_i, y_i));
-
从源点向所有的(x_i)连接一条容量为(1)的边;
-
从所有的(y_i)向汇点连接一条容量为(1)的边;
(因为每个点最多只能一进一出)
然后,对于从(i)连向(j)的有向边,我们就给(x_i, y_j)连接一条容量为(1)的边。如果跑最大流的时候,用到了这条边,那就说明我们把这两个点(所在的简单路)合并成一条简单路了。
代码:
Dinic算法:
/**
* luogu P2764 https://www.luogu.com.cn/problem/P2764
* Dinic算法
**/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 305;
const int maxm = 12005;
const int INF = 0x3f3f3f3f;
struct Edge {
int to, val, nxt;
}e[maxm];
int numedge, head[maxn], n, m, pre[maxn], nxt[maxn], depth[maxn], ans;
const int s = 0;
const int t = maxn - 1;
inline void AddEdge(int from, int to, int val) {
e[numedge].to = to;
e[numedge].val = val;
e[numedge].nxt = head[from];
head[from] = numedge;
numedge++;
}
bool bfs() {
memset(depth, 0, sizeof(depth));
depth[s] = 1;
queue<int> q;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; ~i; i = e[i].nxt) {
int to = e[i].to;
if (!depth[to] && e[i].val > 0) {
depth[to] = depth[u] + 1;
q.push(to);
}
}
}
return depth[t] != 0;
}
int dfs(int u, int flow) {
if (u == t) return flow;
for (int i = head[u]; ~i; i = e[i].nxt) {
int to = e[i].to;
if (e[i].val > 0 && depth[to] > depth[u]) {
int di = dfs(to, min(flow, e[i].val));
if (di > 0) {
e[i].val -= di;
e[i ^ 1].val += di;
if (to > n) {
pre[to - n] = u;
nxt[u] = to - n;
}
return di;
}
}
}
return 0;
}
int Dinic() {
int res = 0;
while (bfs()) {
int d;
while (d = dfs(s, INF))
res += d;
}
return res;
}
int main() {
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
AddEdge(x, y + n, 1);
AddEdge(y + n, x, 0);
}
for (int i = 1; i <= n; i++) {
pre[i] = i;
nxt[i] = i;
AddEdge(s, i, 1);
AddEdge(i, s, 0);
AddEdge(i + n, t, 1);
AddEdge(t, i + n, 0);
}
ans = n;
ans -= Dinic();
for (int i = head[s]; ~i; i = e[i].nxt) {
int to = e[i].to;
if (pre[to] == to) {
int u = to;
for (u = to; nxt[u] != u; u = nxt[u])
printf("%d ", u);
printf("%d
", u);
}
}
printf("%d", ans);
return 0;
}
匈牙利算法:
/**
* luogu P2764 https://www.luogu.com.cn/problem/P2764
* 匈牙利算法
**/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 305;
const int maxm = 12005;
struct Edge {
int to, nxt;
}e[maxm];
int numedge, head[maxn], vis[maxn], match[maxn], pre[maxn], nxt[maxn], n, m, ans;
inline void AddEdge(int from, int to) {
e[numedge].to = to;
e[numedge].nxt = head[from];
head[from] = numedge;
numedge++;
}
bool dfs(int u, int tag) {
if (vis[u] == tag) return false;
vis[u] = tag;
for (int i = head[u]; ~i; i = e[i].nxt) {
int to = e[i].to;
if (!match[to] || dfs(match[to], tag)) {
match[to] = u;
return true;
}
}
return false;
}
int main() {
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
AddEdge(x, y + n);
}
for (int i = 1; i <= n; i++) {
if (dfs(i, i)) ans++;
}
for (int i = 1; i <= n; i++)
pre[i] = nxt[i] = i;
for (int i = 1; i <= n; i++) {
if (match[i + n]) {
pre[i] = match[i + n];
nxt[match[i + n]] = i;
}
}
for (int i = 1; i <= n; i++) {
if (pre[i] == i) {
int u;
for (u = i; nxt[u] != u; u = nxt[u])
printf("%d ", u);
printf("%d
", u);
}
}
printf("%d", n - ans);
return 0;
}