题意:有N个点,M条边(有重边)的无向图,这样图中会可能有桥,问加一条边后,使桥最少,求该桥树。
思路:这个标准想法很好想到,缩点后,求出图中的桥的个数,然后重建图必为树,求出树的最长直径,在该直径的两端点连一边,则图中的桥会最少。
从这题中学到两点,所以写一下解题报告。
1.官方说judge的栈小,得手动增栈 #pragma comment(linker,"/STACK:102400000,102400000") 以前没见过,算是学习了。
2.对改正了对Tarjan算法的一个错误理解,以前看某人博客说,无向图中,Tarjan后low值相等的点属于同一块,以前这样判断过,也过了挺多题。但跟别人讨论后发现是错的。。。。。
思路:这个标准想法很好想到,缩点后,求出图中的桥的个数,然后重建图必为树,求出树的最长直径,在该直径的两端点连一边,则图中的桥会最少。
从这题中学到两点,所以写一下解题报告。
1.官方说judge的栈小,得手动增栈 #pragma comment(linker,"/STACK:102400000,102400000") 以前没见过,算是学习了。
2.对改正了对Tarjan算法的一个错误理解,以前看某人博客说,无向图中,Tarjan后low值相等的点属于同一块,以前这样判断过,也过了挺多题。但跟别人讨论后发现是错的。。。。。
3.以前没用Tarjan写过无向图,不懂正向边访问过,标志反向边,这次学习了。
- //937MS 41304K
- #pragma comment(linker, "/STACK:102400000,102400000")
- #include
- #include
- const int VM = 200005;
- const int EM = 1000005;
- struct Edeg
- {
- int to,nxt,vis;
- }edge[EM<<1],tree[EM<<1];
- int head[VM],vis[VM],thead[VM];
- int dfn[VM],low[VM],stack[VM],belong[VM];
- int ep,bridge,son,maxn,src,n,cnt,scc,top;
- int max (int a,int b)
- {
- return a > b ? a : b;
- }
- int min(int a ,int b)
- {
- return a > b ? b : a;
- }
- void addedge (int cu,int cv)
- {
- edge[ep].to = cv;
- edge[ep].vis = 0;
- edge[ep].nxt = head[cu];
- head[cu] = ep ++;
- edge[ep].to = cu;
- edge[ep].vis = 0;
- edge[ep].nxt = head[cv];
- head[cv] = ep ++;
- }
- void Buildtree(int cu,int cv)
- {
- tree[son].to = cv;
- tree[son].nxt = thead[cu];
- thead[cu] = son ++;
- }
- void Tarjan (int u)
- {
- int v;
- vis[u] = 1;
- dfn[u] = low[u] = ++cnt;
- stack[top++] = u;
- for (int i = head[u];i != -1;i = edge[i].nxt)
- {
- v = edge[i].to;
- if (edge[i].vis) continue; //
- edge[i].vis = edge[i^1].vis = 1; //正向边访问过了,反向边得标志,否则两点会成一块。
- if (vis[v] == 1)
- low[u] = min(low[u],dfn[v]);
- if (!vis[v])
- {
- Tarjan (v);
- low[u] = min(low[u],low[v]);
- if (low[v] > dfn[u])
- bridge ++;
- }
- }
- if (dfn[u] == low[u])
- {
- ++scc;
- do{
- v = stack[--top];
- vis[v] = 0;
- belong[v] = scc;
- }while (u != v);
- }
- }
- void BFS(int u)
- {
- int que[VM+100];
- int front ,rear,i,v;
- front = rear = 0;
- memset (vis,0,sizeof(vis));
- que[rear++] = u;
- vis[u] = 1;
- while (front != rear)
- {
- u = que[front ++];
- front = front % (n+1);
- for (i = thead[u];i != -1;i = tree[i].nxt)
- {
- v = tree[i].to;
- if (vis[v]) continue;
- vis[v] = 1;
- que[rear++] = v;
- rear = rear%(n+1);
- }
- }
- src = que[--rear];//求出其中一个端点
- }
- void DFS (int u,int dep)
- {
- maxn = max (maxn,dep);
- vis[u] = 1;
- for (int i = thead[u]; i != -1; i = tree[i].nxt)
- {
- int v = tree[i].to;
- if (!vis[v])
- DFS (v,dep+1);
- }
- }
- void solve()
- {
- int u,v;
- memset (vis,0,sizeof(vis));
- cnt = bridge = scc = top = 0;
- Tarjan (1);
- memset (thead,-1,sizeof(thead));
- son = 0;
- for (u = 1;u <= n;u ++) //重构图
- for (int i = head[u];i != -1;i = edge[i].nxt)
- {
- v = edge[i].to;
- if (belong[u]!=belong[v])
- {
- Buildtree (belong[u],belong[v]);
- Buildtree (belong[v],belong[u]);
- }
- }
- maxn = 0; //最长直径
- BFS(1); //求树直径的一个端点
- memset (vis,0,sizeof(vis));
- DFS(src,0); //求树的最长直径
- printf ("%d\n",bridge-maxn);
- }
- int main ()
- {
- #ifdef LOCAL
- freopen ("in.txt","r",stdin);
- #endif
- int m,u,v;
- while (~scanf ("%d%d",&n,&m))
- {
- if (n == 0&&m == 0)
- break;
- memset (head,-1,sizeof(head));
- ep = 0;
- while (m --)
- {
- scanf ("%d%d",&u,&v);
- addedge (u,v);
- }
- solve();
- }
- return 0;
- }