题目大意
给定一个强连通的有向图,n个点,m条边;要求,去掉m-2n条边,使剩下的图仍然强连通。
题目分析
采用DFS,将需要保留下的边打上记号。然后任意输出m-2n条不需要保留的边即可。
哪些边需要保留呢?
DFS前进的那些边(即搜索树中的边,“实边”)一定要保留,至于虚边,则保留得越少越好。
所以优先从搜索树的靠叶子端取虚边。比如DFS依次经过1->2->3->4, 我们就先从4开始取回去的边,回去得越前越好,即如果同时存在(4,3)和(4,2)就取(4,2),然后回到结点2,再取一条(2,1)就能构成一个环,也即强连通的子图。
具体实现的话,dfs返回当前访问子树能够回去的最前结点(有点类似tarjan的lowlink)。对一个结点来说,所有虚边只需考虑最优那条(即返回的点index越小越好)。然后拿它跟子树返回结果的最小值(代表子树能够回去的最前结点)比较,如果这条虚边更有,就连上。
P.S. 1A这题,很开心!
source code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <map> #include <cstdio> #include <cstring> #include <vector> #include <utility> #include <algorithm> using namespace std; int n,m; const int maxn = 100020; vector<int> G[maxn]; typedef pair<int, int> P; map<P, int> M; bool used[maxn]; int index[maxn], cnt; int dfs(int v) { index[v] = cnt++; used[v] = true; int bv = v, nbi = index[v]; for (auto i: G[v]) { if (!used[i]) { M[P(v,i)] = 1; nbi = min(nbi, dfs(i)); } else if (index[i] < index[bv]) bv = i; } if (index[bv] < nbi) { M[P(v, bv)] = 1; } return min(nbi, index[bv]); } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); M.clear(); cnt = 0; memset(used, 0, sizeof(used)); memset(index, 0, sizeof(index)); for (int i = 1; i <= n; i++) G[i].clear(); for (int i = 0; i < m; i++) { int x,y; scanf("%d%d", &x, &y); G[x].push_back(y); M[P(x,y)] = 0; } dfs(1); int tot = 0; for (auto i: M) { if (!i.second) { tot++; printf("%d %d\n", i.first.first, i.first.second); if (tot == m - 2 * n) break; } } } }
|
Last updated: