HDU 4612 Warm up —— (缩点 + 求树的直径)
Posted Storm_Spirit
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 4612 Warm up —— (缩点 + 求树的直径)相关的知识,希望对你有一定的参考价值。
题意:一个无向图,问建立一条新边以后桥的最小数量。
分析:缩点以后,找出新图的树的直径,将这两点连接即可。
但是题目有个note:两点之间可能有重边!而用普通的vector保存边的话,用v!=fa的话是没办法让重边访问的,因此,使用数组模拟邻接表的方法来储存边。
这样,只要访问了一条边以后,令E[i].vis=E[i^1].vis=1即可,这样可以防止无向图的边和重边搞混。原理就是按位异或,一个奇数^1变为比它小1的偶数,反之亦然:如5^1=4,4^1=5。
具体见代码:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <stack> 5 #include <vector> 6 #include <set> 7 #include <queue> 8 //#pragma comment(linker, "/STACK:1024000000,1024000000") 9 using namespace std; 10 11 const int N = 200000+5; 12 13 int n,m,dfn[N],low[N]; 14 int head[N]; 15 int tot=0; 16 int dfs_clock; 17 int bridge; 18 int belong[N]; 19 int scc_cnt; 20 int maxd; 21 bool vis[N]; 22 vector<int> G[N]; 23 stack<int> S; 24 25 struct edge 26 { 27 int v; 28 int vis; 29 int nxt; 30 }E[2*1000000+10]; 31 32 void addEdge(int u,int v) 33 { 34 E[tot].v=v; 35 E[tot].vis=0; 36 E[tot].nxt=head[u]; 37 head[u]=tot++; 38 39 E[tot].v=u; 40 E[tot].vis=0; 41 E[tot].nxt=head[v]; 42 head[v]=tot++; 43 } 44 45 void tarjan(int u) 46 { 47 dfn[u]=low[u]=++dfs_clock; 48 S.push(u); 49 for(int i=head[u];i!=-1;i=E[i].nxt) 50 { 51 int v = E[i].v; 52 if(E[i].vis) continue; 53 E[i].vis=E[i^1].vis=1; 54 55 if(!dfn[v]) 56 { 57 tarjan(v); 58 low[u]=min(low[u],low[v]); 59 60 if(low[v]>dfn[u]) bridge++; 61 } 62 else if(!belong[v]) 63 { 64 low[u]=min(low[u],dfn[v]); 65 } 66 } 67 68 if(dfn[u]==low[u]) 69 { 70 scc_cnt++; 71 for(;;) 72 { 73 int x = S.top();S.pop(); 74 belong[x] = scc_cnt; 75 if(x==u) break; 76 } 77 } 78 } 79 80 void init() 81 { 82 memset(head,-1,sizeof(head)); 83 tot=0; 84 memset(dfn,0,sizeof(dfn)); 85 dfs_clock=0; 86 bridge=0; 87 memset(belong,0,sizeof(belong)); 88 scc_cnt=0; 89 maxd=0; 90 for(int i=1;i<=n;i++) G[i].clear(); 91 memset(vis,0,sizeof(vis)); 92 } 93 94 //找到树的直径 95 void findMaxDeep(int u,int deep) 96 { 97 vis[u]=1; 98 99 maxd=max(maxd,deep); 100 for(int i=0;i<G[u].size();i++) 101 { 102 int v = G[u][i]; 103 if(!vis[v]) 104 { 105 findMaxDeep(v,deep+1); 106 } 107 } 108 } 109 110 //用bfs来找到一个叶子节点 111 int findRoot() 112 { 113 queue<int> Q; 114 Q.push(1); 115 vis[1]=1; 116 int last=1; 117 while(!Q.empty()) 118 { 119 int x = Q.front();Q.pop(); 120 for(int i=0;i<G[x].size();i++) 121 { 122 int v = G[x][i]; 123 if(!vis[v]) 124 { 125 Q.push(v); 126 vis[v]=1; 127 last=v; 128 } 129 } 130 } 131 return last; 132 } 133 134 void solve() 135 { 136 for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); 137 138 //重新建图 139 for(int i=1;i<=n;i++) 140 { 141 for(int j=head[i];j!=-1;j=E[j].nxt) 142 { 143 int v = E[j].v; 144 int x = belong[i]; 145 int y = belong[v]; 146 if(x!=y) 147 { 148 G[x].push_back(y); 149 G[y].push_back(x); 150 } 151 } 152 } 153 154 int root=findRoot(); 155 memset(vis,0,sizeof(vis)); 156 157 findMaxDeep(root,0); 158 159 printf("%d\n",bridge-maxd); 160 } 161 162 int main() 163 { 164 while(scanf("%d%d",&n,&m)==2) 165 { 166 if(n==0 && m==0) break; 167 init(); 168 169 for(int i=1;i<=m;i++) 170 { 171 int u,v; 172 scanf("%d%d",&u,&v); 173 addEdge(u,v); 174 } 175 176 solve(); 177 } 178 }
但是奇怪的是,重新建图以后找叶子节点的时候,这里用G[x].size()==2不能够实现。讲道理,原理上是没错的,尽管后来发现如果缩点后只有一个点的话是个例外,但是即使排除了这个特殊情况仍然不行,,,这个问题也留着以后探讨吧。。
以上是关于HDU 4612 Warm up —— (缩点 + 求树的直径)的主要内容,如果未能解决你的问题,请参考以下文章
HDU 4612 Warm up(双连通分量缩点+求树的直径)
[HDOJ4612]Warm up(双连通分量,缩点,树直径)