1. 程式人生 > >天天寫演算法之離線Tarjan演算法Closest Common Ancestors

天天寫演算法之離線Tarjan演算法Closest Common Ancestors

這些延伸的題目都是我在做hdu的時候由於原題比較難,所以才會找更加基礎的來做。對於ACM小白來說,基本上是每隔4道題就必然有一個新的題型。今天就是離線Tarjan演算法。emmmLCA我還沒完全寫對。我要對這個教程裡沒有闡述的東西進行一下深剖。比如       5    4       3        2 1    6這個地方是4 3 2 是5的兒子,1 6 是4的兒子這裡用到了並查集,首先我們起初的時候要把所有的pre【i】=i,就是所有節點的祖先節點都是本身。這一點很重要,不單單是並查集的應用那麼簡單。

上程式碼:

void dfs(int u)
{
   for(int i = 1; i <= n; i++)
   {
      if(visit[i]&&ask[u][i])
      {
        LCA[u][i] = Find(i);
      }
   }
   visit[u] = true;
   for(int i = 0; i < g[u].size(); i++)
   {
     int son = g[u][i];
     dfs(son);
     father[son] = u;
   }
}
visit表示是否被訪問了。ask表示詢問的節點結果,比如我問你1和6的最近祖先是什麼。g是一個vector的陣列,儲存的是g[i]儲存的是i這個節點所有的兒子。
首先再賦值的時候也就是ask的時候我們要ask[i][u]和ask[u][i]都要標記上,因為假如你第一遍走到ask[i][u]發現i沒有訪問過,那麼問題就來了,這個ask就再也不會被訪問了,所以需要另一個等到訪問到i的時候就會發現u被訪問了,就能輸出結果了。另一個點,就是在執行 dfs(son)之後,才會把這些孩子的父親設定為離他最近的父親,要知道在這之前他們所有節點的父親都是他自己。
比如問我1 6 的祖先,此時已經找到了6,這個時候1已經訪問過了,訪問過了代表什麼呢,代表他的father被設定成了4,此時還沒訪問完4的所有兒子,說明還沒有dfs(4)還沒有完成,那麼father(4)也就還是等於4.所以能輸出正確結果,他是依靠這個特性保持最近父節點的。加入我們再找1 3 的dfs(4)已經回來了,說明了什麼?說明father(4)已經被設定成了5,此時find(4)就等於了5.程式碼來一波:
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 1000;
int ask[maxn][maxn];//儲存詢問
int ans[maxn];//儲存祖先i出現過的次數
int n,m;
vector<int> g[maxn];//儲存兒子
int root;//樹的根
bool visit[maxn];
bool isroot[maxn];
int father[maxn];
int Find(int x)
{
    if(father[x] == x) return x;
    else return father[x] = Find(father[x]);
}
void init()
{
    memset(ans,0,sizeof(ans));
    memset(visit,false,sizeof(visit));
    memset(isroot,true,sizeof(isroot));
    memset(ask,0,sizeof(ask));
    for(int i = 1; i <= n; i++)
    {
        g[i].clear();
        father[i] = i;
    }

}
void LCA(int root)
{
    for(int i = 1; i <= n; i++)
    {
        if(visit[i]&&ask[root][i])
        {
            ans[Find(i)] += ask[root][i];
        }
    }
    visit[root] = true;
    for(int i = 0; i < g[root].size(); i++)
    {
        int term = g[root][i];
        LCA(term);
        father[term] = root;
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        init();
        int f,s,num;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d:(%d)",&f,&num);
            for(int j = 1; j <= num; j++)
            {
                scanf(" %d",&s);
                isroot[s] = false;
                g[f].push_back(s);
            }
        }
        for(int i = 1; i <= n; i++)
        {
            if(isroot[i])
            {
                root = i;
                break;
            }
        }
        scanf("%d",&m);
        int u,v;
        for(int i = 1; i <= m; i++)
        {
            scanf(" (%d %d)",&u,&v);
            ask[u][v]++;
            ask[v][u]++;
        }
        LCA(root);
        for(int i = 1; i <= n; i++)
        {
            if(ans[i])
            {
                printf("%d:%d\n",i,ans[i]);
            }
        }
    }
    return 0;
}