1. 程式人生 > >poj3694 tarjan求橋

poj3694 tarjan求橋

#include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; int n,m,ans; int deep[200005],low[200005],fa[200005],pre[200005],tim; vector<int> G[200005]; int find(int x) {     if(pre[x]==x)     {         return x;     }     return pre[x]=find(pre[x]); } bool unite(int x,int y) {     int fx=find(x);     int fy=find(y);     if(fx==fy)     {         return false;     }     pre[fx]=fy;     return true; } void tarjan(int x,int father) {     fa[x]=father;     deep[x]=low[x]=++tim;     for(int i=0;i<G[x].size();i++)     {         int y=G[x][i];         if(y==father)         {             continue;         }         if(deep[y]==0)         {             tarjan(y,x);             low[x]=min(low[x],low[y]);             if(deep[x]<low[y])             {                 ans++;             }             else             {                 unite(x,y);             }         }         else         {             low[x]=min(low[x],deep[y]);         }     } } int lca(int x,int y) {     if(deep[x]<deep[y])     {         int tmp=x;         x=y;         y=tmp;     }     while(deep[x]>deep[y])     {         if(unite(x,fa[x]))         {             ans--;         }         x=fa[x];     }     while(x!=y)     {         if(unite(y,fa[y]))         {             ans--;         }         y=fa[y];     }     return ans; } int main() {     int cae=1;     while(scanf("%d%d",&n,&m)&&!(n==0&&m==0))     {         for(int i=0;i<=100004;i++)         {             G[i].clear();         }         memset(deep,0,sizeof(deep));         memset(low,0,sizeof(low));         memset(fa,0,sizeof(fa));         memset(pre,0,sizeof(pre));         tim=ans=0;         for(int i=1;i<=m;i++)         {             int x,y;             scanf("%d%d",&x,&y);             G[x].push_back(y);             G[y].push_back(x);         }         for(int i=1;i<=n;i++)         {             pre[i]=i;         }         tarjan(1,1);         int q;         scanf("%d",&q);         printf("Case %d:\n",cae);         for(int i=1;i<=q;i++)         {             int x,y;             scanf("%d%d",&x,&y);             printf("%d\n",lca(x,y));         }         printf("\n");          cae++;     }     return 0; }

對於一個環來說是不存在橋的,因為無論刪除環中的任意一條邊,這些點還是互相聯通的(因為是無向邊)。但是如果一條無向邊連線的兩個點不在一個環裡(重邊不算),那麼刪除這條邊就會導致這兩個點不再聯通。所以可以使用tarjan演算法求環。基於dfs給各個點打上時間戳。deep[x]表示的是深搜到這x點的順序,low[x]表示的是這一點所能回溯連線到所有點中最早的點的編號。fa[x]表示的是x點在dfs中的上一個點。而pre表示的是並查集的陣列。這裡我們使用並查集的方式來記錄哪些點屬於於同一個環。

lca在這裡面的作用是一步一步跳,把所有的橋找出來並且消除。