一.原题链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1588
二.题目大意:给你一个图,让你求出每一条割边。
三.思路:Tarjan求割边,注意判断有没有重边,意味着要标记,而且数据量太大要用邻接表。判断重边我采用遍历的方法,然后就是传统的对于一个节点u,如果存在一个深度优先搜索树的儿子v使得low[v]>dfn[u],则边(u,v)为割边。注意在求割点的时候是low[v]>=dfn[u],这时重边也都没有关系,等号的成立的情况其实就是重边使得回边从v->u,但是割边的话重边一定就不能是割边了,虽然我把重边先全部去除了。
Tarjan算法详情:http://blog.csdn.net/h992109898/article/details/51318135
吐槽一下:没见过这么恶心的输出格式控制,末尾空格,最后一个样例的空格都卡。
四.代码:
#include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <cmath> #include <algorithm> #include <vector> using namespace std; const int MAX_N = 10005, INF = 0x3f3f3f3f; struct Edge { int u, v, id; bool isRepeat; }; vector <Edge> edges[MAX_N]; vector <Edge> res; int edgeNum, nodeNum, head[MAX_N], low[MAX_N], dfn[MAX_N]; bool visited[MAX_N]; void init() { int i; memset(visited, 0, sizeof(visited)); for(i = 0; i < MAX_N; i++) edges[i].clear(); res.clear(); } void addEdge(int u, int v, int id) { int i; Edge tmp; tmp.u = u, tmp.v = v, tmp.id = id; tmp.isRepeat = false; //判重边 for(i = 0; i < edges[u].size(); i++) if(v == edges[u][i].v){ edges[u][i].isRepeat = true; return; } edges[u].push_back(tmp); } void dfs(int pre, int u, int depth) { visited[u] = true; low[u] = dfn[u] = depth; int i, v; for(i = 0; i < edges[u].size(); i++){ v = edges[u][i].v; if(!visited[v]){ dfs(u, v, depth + 1); low[u] = min(low[u], low[v]); if(low[v] > dfn[u] && !edges[u][i].isRepeat){ res.push_back(edges[u][i]); } } else if(v != pre) low[u] = min(low[u], dfn[v]); } } bool cmp(Edge a, Edge b) { return a.id < b.id; } int main() { //freopen("in.txt", "r", stdin); int test, u, v, i; scanf("%d", &test); while(test--){ init(); scanf("%d%d", &nodeNum, &edgeNum); for(i = 0; i < edgeNum; i++){ scanf("%d%d", &u, &v); addEdge(u, v, i + 1); addEdge(v, u, i + 1); } dfs(1, 1, 1); int num = res.size(); sort(res.begin(), res.end(), cmp); printf("%d\n", num); if(num){ printf("%d", res[0].id); for(i = 1; i < num; i++) printf(" %d", res[i].id); printf("\n"); } if(test) printf("\n"); } return 0; }