OJ题号:
洛谷2860、POJ3177
题目大意:
给定一个无向图,试添加最少的边使得原图中没有桥。
思路:
Tarjan缩点,然后统计度为$1$的连通分量的个数(找出原图中所有的桥)。
考虑给它们每两个连通分量连一条边,这样一次性可以解决两个。
如果最后还有多的,就专门给它随便连一条边。
设度为$1$的连通分量的个数为$c$,则答案为$\lfloor{\frac{c+1}{2}}\rfloor$。
因为是无向图,所以用一般的Tarjan会来回走同一条边,这样就会把桥的两岸缩在同一个点中,不合题意。
考虑Tarjan中记录当前结点的父亲结点,往下递归时判断是否与其相等。这样看起来是正确的,但是交到洛谷上会WA一个点。
因为原图中不一定保证相同的两个点之间只有一条边,因此如果当某两点间同时存在两条边时,不能算桥。但是按照上面的算法不会将这两点缩在一起。
考虑记录每条边的编号,每次递归判断枚举到的出边是否与入边编号相等即可。
1 #include<stack> 2 #include<cstdio> 3 #include<cctype> 4 #include<vector> 5 inline int getint() { 6 char ch; 7 while(!isdigit(ch=getchar())); 8 int x=ch^‘0‘; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^‘0‘); 10 return x; 11 } 12 const int V=5001; 13 struct Edge { 14 int to,id; 15 }; 16 std::vector<Edge> e[V]; 17 inline void add_edge(const int u,const int v,const int id) { 18 e[u].push_back((Edge){v,id}); 19 } 20 int dfn[V]={0},low[V]={0},scc[V]={0},cnt=0,id=0; 21 bool ins[V]={0}; 22 std::stack<int> s; 23 void Tarjan(const int x,const int eid) { 24 dfn[x]=low[x]=++cnt; 25 s.push(x); 26 ins[x]=true; 27 for(unsigned i=0;i<e[x].size();i++) { 28 if(e[x][i].id==eid) continue; 29 int &y=e[x][i].to; 30 if(!dfn[y]) { 31 Tarjan(y,e[x][i].id); 32 low[x]=std::min(low[x],low[y]); 33 } 34 else if(ins[y]) { 35 low[x]=std::min(low[x],dfn[y]); 36 } 37 } 38 if(low[x]==dfn[x]) { 39 int y; 40 id++; 41 do { 42 y=s.top(); 43 s.pop(); 44 ins[y]=false; 45 scc[y]=id; 46 } while(y!=x); 47 } 48 } 49 int deg[V]={0}; 50 int main() { 51 int n=getint(); 52 for(int m=getint();m;m--) { 53 int u=getint(),v=getint(); 54 add_edge(u,v,m); 55 add_edge(v,u,m); 56 } 57 for(int i=1;i<=n;i++) { 58 if(!dfn[i]) Tarjan(i,0); 59 } 60 for(int x=1;x<=n;x++) { 61 for(unsigned i=0;i<e[x].size();i++) { 62 int &y=e[x][i].to; 63 if(scc[x]!=scc[y]) deg[scc[x]]++,deg[scc[y]]++; 64 } 65 } 66 int cnt=0; 67 for(int i=1;i<=id;i++) { 68 if(deg[i]==2) cnt++; 69 } 70 printf("%d\n",(cnt+1)>>1); 71 return 0; 72 }
时间: 2024-10-11 17:30:06