【图论好题】ABC #142 Task F Pure

题目大意

给定一个 $N$ 个点 $M$ 条边的有向图 $G$,无重边、自环。找出图 $G$ 的一个导出子图(induced subgraph) $G'$,且 $G'$ 中的每个点的入度和出度都是 1。

数据范围

  • $ 1 \le N \le 1000$
  • $ 0 \le M \le 2000$

分析

导出子图 $G'$ 中的每个点的入度和出度都是 1 相当于说 $G'$ 是一个(cycle)。

若不考虑 $G'$ 是导出子图这个条件,则可通过 DFS 判断图 $G$ 中是否有环,若有,同时还可以找出一个环。

下面给出环的一个性质:
若有向图 $C$ 是一个环,则往 $C$ 中添一条边将产生一个更小的环。

读者自证不难。

因此若有向图 $G$ 中有环,则 $G$ 中的最小环必然是 $G$ 的导出子图。

于是原问题可以转化成求图 $G$ 中的最小环。

更进一步,可以先 DFS 找出一个环 $C$,设 $C$ 的点集是 $V_C$;接着在由 $V_C$ 导出的子图 $G_C$ 上找最一个最小环 $C'$,则 $C'$ 是 $G_C$ 的导出子图,从而也是 $G$ 的导出子图。

上述论证用到了导出子图的一个性质:
若 $B$ 是 $A$ 的导出子图,$C$ 是 $B$ 的导出子图,则 $C$ 也是 $A$ 的导出子图。

Implementation

下列代码是最暴力的实现。

int n, m; scan(n, m);
if (n == 1) {
    println(-1);
    return 0;
}

vv<int> g(n);
rep (m) {
    int a, b; scan(a, b); --a, --b;
    g[a].pb(b);
}

vb used(n);
vb vis(n);
int lim;
int root;
bool flag;

function<void(int,int)> dfs = [&](int u, int d) {
    vis[u] = true;
    if (d == lim) {
        FOR (v, g[u]) {
            if (v == root) {
                flag = true;
                println(d + 1);
                break;
            }
        }
    }
    else {
        FOR (v, g[u]) {
            if (!vis[v] && !used[v]) {
                dfs(v, d + 1);
                if (flag) break; // 别忘了这里的 break
            }
        }
    }
    if (flag) println(u + 1);
};

for (lim = 1; lim < n; ++lim) {
    fill(all(used), false);
    rng (i, 0, n) {
        fill(all(vis), false);
        flag = false;
        root = i;
        dfs(i, 0);
        if (flag) {
            return 0;
        }
        used[i] = true;
    }
}

println(-1);

猜你喜欢

转载自www.cnblogs.com/Patt/p/11605688.html
142