LC离线算法实现历程

这个没别的。。。只是记录我学习LCA的历程而已 。。。

/* 写了个有bug的LAC(应该不算), 就是建树搜索的时候必须要以1为节点 ,然后开始搜索(讲道理这个缺陷已经很大了。。。)
算是我实现LAC的第一步吧*/ 
#include<bits/stdc++.h>
using namespace std ;
vector<int> q[1000 + 5] ;
int s , t ;     int n ;
int vis[10500] ;
int pre[10500] ;
int LAC[105500] ;
void init(){
 for(int i = 0 ; i <=n ; i++){
    pre[i] = i ;
    q[i].clear() ;
    vis[i] = 0 ;
 }

}
int find(int x)
{
	return pre[x]==x?pre[x]:pre[x]=find(pre[x]);
}
void Union(int u , int ui){
    if(find(u) != find(ui) ) {
        pre[ui]=u ;
    }
}
/* 树的节点从1开始*/
void solve(int x){
 int len  = q[x].size() ;
   for(int i = 0 ; i < len ; i++){
      solve(q[x][i]) ;
      Union(x , q[x][i]) ;
  //   pre[q[x][i]] = x ;
    // vis[x] =  1   ;
   }
   vis[x] = 1 ;
   /*if(vis[s] == vis[t] && vis[s] == 1 ){

   }*/
   if(x == s && vis[t] == 1){
       printf("公共祖先为: %d\n" ,find(t) );
   }
   if(x == t && vis[s] == 1 ){
    printf("公共祖先为 %d\n", find(s) ) ;
   }
}
int main(){

  cin >> n ;
  init() ;
  for(int i = 0 ; i <n - 1 ; i++){
        int u , v ;
     cin >> u >> v ;
     q[u].push_back(v) ;
  //   q[v].push_back(u) ;
  }

   cin >> s >> t ;

   solve(1) ;

 return 0 ;
}

之后加了个双向边 , 求的也是最近的公共祖先
然后发现,其实对于一棵树,你可以以任何一个节点来作为顶点,
只是这样子做只是树的深度和公共祖先不同而已,如果对于一个无根树
可以以任何点来作为顶点,这样子求两点之间的最短距离其实是一样的。

#include<bits/stdc++.h>
using namespace std ;
vector<int> q[1000 + 5] ;
int s , t ;     int n ;
int vis[10500] ;
int pre[10500] ;

int LAC[105500] ;
void init(){
 for(int i = 0 ; i <=n ; i++){
    pre[i] = i ;
    q[i].clear() ;
    vis[i] = 0 ;
 }

}
int find(int x)
{
	return pre[x]==x?pre[x]:pre[x]=find(pre[x]);
}
void Union(int u , int ui){
    if(find(u) != find(ui) ) {
        pre[ui]=u ;
    }
}
/* 树的节点从1开始*/
void solve(int x){
 int len  = q[x].size() ;
 LAC[x] = 1 ;
   for(int i = 0 ; i < len ; i++){
         if(LAC[q[x][i]] == 0 ){
      solve(q[x][i]) ;
      Union(x , q[x][i]) ;
         }
  //   pre[q[x][i]] = x ;
    // vis[x] =  1   ;
   }
   vis[x] = 1 ;
   /*if(vis[s] == vis[t] && vis[s] == 1 ){

   }*/
   if(x == s && vis[t] == 1){
       printf("公共祖先为: %d\n" ,find(t) );
   }
   if(x == t && vis[s] == 1 ){
    printf("公共祖先为 %d\n", find(s) ) ;
   }
}
int main(){

  cin >> n ;
  init() ;
  for(int i = 0 ; i <n - 1 ; i++){
        int u , v ;
     cin >> u >> v ;
     q[u].push_back(v) ;
     q[v].push_back(u) ;
  }

   cin >> s >> t ;

   solve(1) ;

 return 0 ;
}

学会求公共节点以后,开始求无根树的任意两点距离
然后WA了7,8次后终于AC了。。。

/*我居然没看模板23333333*/ 
#include<string>
#include<cstring>
#include<iostream>
#include<vector>
#include<cstdio>

using namespace std ;
int n , m ;
const int maxn = 400500;
int head[maxn] ; // 链式前向星
int dis[maxn] ; // 用于记录路径之和(用法; 有头节点一直往下累加 , dis[v] 表示从节点1到v的距离)
int pre[maxn] ; // 并查集函数
int LAC[maxn] ; // 用于记录公共祖先是谁
int _head[maxn] ;
int ans[maxn] ,  ans1[maxn] ;
int vis[maxn] ; // vis标记 ,因为是双向边,不加vis标记会造成死循环

struct edge{
 int to , next ,  w ;
}edge[maxn ];
struct node{
  int to , next , num ;
}st[maxn];
int cnt = 0  , tot = 0;
int find(int x) // 并查集函数
{
      if(pre[x]==x)  return x;
      return pre[x]=find(pre[x]);
}
void init(){
  cnt = 0 ;
  tot = 0 ;
  memset(head , -1 , sizeof(head)) ;
  memset(dis , 0 , sizeof(dis)) ;
  memset(vis , 0 , sizeof(vis)) ;
  memset(_head , -1 , sizeof(_head)) ;
  memset(LAC , 0 , sizeof(LAC) ) ;
  memset(ans , 0 , sizeof(ans)) ;
  memset(ans1 , 0 , sizeof(ans1)) ;
  for(int i = 1 ; i <= n ; i++) pre[i] = i ;
}
void add(int u , int v , int  w){
  edge[++cnt].to = v ;
  edge[cnt].w = w ;
  edge[cnt].next = head[u] ;
  head[u] = cnt ;
}
void add_pro(int u , int v , int num ) {
   st[++tot].to = v ;
   st[tot].num = num ;
   st[tot].next = _head[u] ;
   _head[u] = tot ;
}
void solve(int u){
    vis[u] = 1 ;
    pre[u] = u ; //

    for(int i = head[u] ; i != -1 ; i = edge[i].next ){
         int v = edge[i].to ;
         if(!vis[v]){
          dis[v] = edge[i].w + dis[u] ; // 求出1节点到 v节点的距离 (就是这里没注意顺序WA了好多次。。。)
          solve(v) ;
          pre[v] = u ; // Union函数 因为是树就不需要判断了

         // cout << dis[v] <<endl ;
         }
    }
    for(int i = _head[u] ; i != -1 ; i = st[i].next){
         int v = st[i].to ;
         int num = st[i].num ;
         if(vis[v]){
            LAC[num] = find(v) ;
           // cout << "***" <<endl ;
         }
    }
}
int main(){
    int t ; cin >> t ;
    while(t--){
            init() ;
  scanf("%d %d",&n,&m ) ;
  int u , v , w ;
     for(int i = 0 ; i < n- 1 ; i++){
         scanf("%d %d %d",&u,&v,&w) ;
         add(u , v, w ) ;
         add(v , u, w ) ;
     }
     for(int i = 0 ; i < m ; i++){
         scanf("%d %d",&u,&v) ;
         add_pro(u , v, i) ;
         add_pro(v , u, i) ;
         ans[i] = u , ans1[i] = v ; // 这个用于记录问题的答案, 因为add_pro这个函数相当于构图
     }
     solve(1);
 /*  for(int i = 0 ; i < 10 ; i++){
        cout << dis[i] << " " ;
     } cout <<endl ;
*/
      for(int i = 0 ; i < m ; i++){
        //    cout << dis[ans[i]] << " " << dis[ans1[i]] << " " <<dis[LAC[i]] << endl ;
        printf("%d\n",dis[ans[i]] + dis[ans1[i]] - 2 * dis[LAC[i]]) ;
        // 有dis数组的意义来理解
      }
    }
 return 0 ;
}

猜你喜欢

转载自blog.csdn.net/qq_42894605/article/details/82779107
今日推荐