倍增LCA code[vs]1036商务旅行
n個點用n-1條邊連接,求兩個點間的最短路
顯然可以想到用floyd預處理,但復雜度過高
所以一些巨發明了LCA
為什么這類最短路問題要找最近公共祖先,這是一個顯然的問題,最近公共祖先說簡陋了就是在這個“樹”上找一個“轉折點"
LCA分為離線和在線兩種,這里先寫在線的方法
?
在線LCA運用的思想是倍增
何為倍增?
在我這個渣看來,一個正整數是可以寫成一個或多個n不相同的2^n的和
那么一些時候就可以依據這一特征進行算法優化,log一下復雜度就大大降低了
?
具體的代碼實現大體分三部分
首先是用鄰接表實現的深搜,得到每個點的“深度”(將這個圖看成樹)
需要注意的是for循環中間i的判斷條件與memset初始化的head[]數組值有關
f[edge[i].to][0]記錄的值為其父節點
1 void dfs(int p){ 2 for(int i=head[p];i;i=edge[i].next){ 3 if(!deep[edge[i].to]){ 4 deep[edge[i].to]=deep[p]+1; 5 f[edge[i].to][0]=p; 6 dfs(edge[i].to); 7 } 8 } 9 }?
接著是對f[][]數組預處理
f[i][j] 表示點i向上走2^j步到達的點p?
f[i][j-1] 表示節點i向上走2^(j-1)步到達的節點p'?
p'再向“上”走2^j-2^(j-1)=2^(j-1)步則可到達p
所以可以得到遞推式f[i][j]=f[f[i][j-1]][j-1]
1 void work(){ 2 for(int j=1;(1<<j)<=n;j++) 3 for(int i=1;i<=n;i++) 4 if(f[i][j-1]!=-1) 5 f[i][j]=f[f[i][j-1]][j-1]; 6 }?
最后就是LCA核心
這里的i需要單獨定義出來
注意依據深度aa和bb,一定要從下向上走的吧
1 int lca(int aa,int bb){ 2 int i; 3 if(deep[aa]<deep[bb]) swap(aa,bb); 4 for(i=0;(1<<i)<=deep[aa];i++); 5 i--; 6 for(int j=i;j>=0;j--) 7 if(deep[aa]-(1<<j)>=deep[bb]) 8 aa=f[aa][j]; 9 if(aa==bb) return aa; 10 for(int j=i;j>=0;j--){ 11 if(f[aa][j]!=-1&&f[aa][j]!=f[bb][j]){ 12 aa=f[aa][j]; 13 bb=f[bb][j]; 14 } 15 } 16 return f[aa][0]; 17 }?
附一模板題
1036 商務旅行
時間限制: 1 s
空間限制: 128000 KB
題目等級 : 鉆石 Diamond題目描述?Description
某首都城市的商人要經常到各城鎮去做生意,他們按自己的路線去做,目的是為了更好的節約時間。
假設有N個城鎮,首都編號為1,商人從首都出發,其他各城鎮之間都有道路連接,任意兩個城鎮之間如果有直連道路,在他們之間行駛需要花費單位時間。該國公路網絡發達,從首都出發能到達任意一個城鎮,并且公路網絡不會存在環。
你的任務是幫助該商人計算一下他的最短旅行時間。
輸入描述?Input Description輸入文件中的第一行有一個整數N,1<=n<=30 000,為城鎮的數目。下面N-1行,每行由兩個整數a?和b?(1<=a,?b<=n; a<>b)組成,表示城鎮a和城鎮b有公路連接。在第N+1行為一個整數M,下面的M行,每行有該商人需要順次經過的各城鎮編號。
輸出描述?Output Description在輸出文件中輸出該商人旅行的最短時間。
樣例輸入?Sample Input 5 1 2 1 5 3 5 4 5 4 1 3 2 5 樣例輸出?Sample Output 7 注意一下f[][]數組賦初值的問題就好 貼代碼 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 int n=0,x=0,y=0,cnt=0,head[30010],deep[30010],f[30010][33],m=0,a=0,b=0,ans=0; 7 struct data{ 8 int next,to; 9 }edge[60010]; 10 11 void add(int start,int end){ 12 edge[++cnt].next=head[start]; 13 edge[cnt].to=end; 14 head[start]=cnt; 15 } 16 17 void dfs(int p){ 18 for(int i=head[p];i;i=edge[i].next){ 19 if(!deep[edge[i].to]){ 20 deep[edge[i].to]=deep[p]+1; 21 f[edge[i].to][0]=p; 22 dfs(edge[i].to); 23 } 24 } 25 } 26 27 void work(){ 28 for(int j=1;(1<<j)<=n;j++) 29 for(int i=1;i<=n;i++) 30 if(f[i][j-1]!=-1) 31 f[i][j]=f[f[i][j-1]][j-1]; 32 } 33 34 int lca(int aa,int bb){ 35 int i; 36 if(deep[aa]<deep[bb]) swap(aa,bb); 37 for(i=0;(1<<i)<=deep[aa];i++); 38 i--; 39 for(int j=i;j>=0;j--) 40 if(deep[aa]-(1<<j)>=deep[bb]) 41 aa=f[aa][j]; 42 if(aa==bb) return aa; 43 for(int j=i;j>=0;j--){ 44 if(f[aa][j]!=-1&&f[aa][j]!=f[bb][j]){ 45 aa=f[aa][j]; 46 bb=f[bb][j]; 47 } 48 } 49 return f[aa][0]; 50 } 51 52 int main(){ 53 scanf("%d",&n); 54 memset(head,0,sizeof(head)); 55 memset(deep,0,sizeof(deep)); 56 memset(f,-1,sizeof(f)); 57 for(int i=1;i<n;i++){ 58 scanf("%d%d",&x,&y); 59 add(x,y); 60 add(y,x); 61 } 62 deep[1]=1; 63 dfs(1); 64 work(); 65 66 scanf("%d",&m); 67 scanf("%d",&a); 68 for(int i=1;i<m;i++){ 69 scanf("%d",&b); 70 ans+=deep[a]+deep[b]-2*deep[lca(a,b)]; 71 a=b; 72 } 73 printf("%d\n",ans); 74 return 0; 75 }?
?
轉載于:https://www.cnblogs.com/sdfzxh/p/6670125.html
總結
以上是生活随笔為你收集整理的倍增LCA code[vs]1036商务旅行的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WebM视频格式怎么转换成MP4
- 下一篇: Activity嵌套fragment大全