1. 程式人生 > >【做題】計蒜客11217 百度地圖的實時路況——分治

【做題】計蒜客11217 百度地圖的實時路況——分治

std 單個 sin 其中 %d min urn 百度 得到

又是一道明明簡單我卻不會的題。

切入點當然是O(n^4)的暴力。顯然這其中有大量的重復計算。

一開始的想法是從前往後,從後往前各跑一遍floyd。這樣做的關鍵問題在於如何合並兩個floyd的結果。然而我只想出了O(n^3)的合並。故這種做法除了只有暴力1/6的常數(手算得到)之外,就並沒有什麽卵用了。放棄這個想法。

換言之,我們的做法是需要避免多次添加同一節點。這需要我們對刪去一個點的方案進行分類。

然後我就看了題解。

因為我們的結果是單個點被刪去,所以考慮從從多個點被刪去的狀態轉移過來。

記dis(l,r)的l到r中的節點尚未被添加時任意兩點最短路的集合。我們就可以轉移到dis(l,(l+r)/2)和dis((l+r)/2+1,r)了,並且當前狀態只會被一個狀態轉移過來。故每個點只會被添加一次。

時間復雜度O(n^3)。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=305,INF=1e8;
 4 int dis[10][N][N],n;
 5 long long ans;
 6 void floyd(int l,int r,int now)
 7 {
 8     for(int k=l;k<=r;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
 9         dis[now][i][j]=min(dis[now][i][k]+dis[now][k][j],dis[now][i][j]);
10 } 11 void solve(int l,int r,int now) 12 { 13 if(l==r) 14 { 15 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i!=l&&j!=l) 16 ans+=(dis[now][i][j]==INF?-1:dis[now][i][j]); 17 return; 18 } 19 int mid=(l+r)>>1; 20 for(int i=1;i<=n;i++)for
(int j=1;j<=n;j++) 21 dis[now+1][i][j]=dis[now][i][j]; 22 floyd(l,mid,now+1); 23 solve(mid+1,r,now+1); 24 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) 25 dis[now+1][i][j]=dis[now][i][j]; 26 floyd(mid+1,r,now+1); 27 solve(l,mid,now+1); 28 } 29 int main() 30 { 31 scanf("%d",&n); 32 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) 33 { 34 scanf("%d",&dis[0][i][j]); 35 dis[0][i][j]=(dis[0][i][j]==-1?INF:dis[0][i][j]); 36 } 37 solve(1,n,0); 38 printf("%lld\n",ans); 39 return 0; 40 }

小結:這大概是我對floyd不夠深入了解吧。

【做題】計蒜客11217 百度地圖的實時路況——分治