【做题】计蒜客11217 百度地图的实时路况——分治

又是一道明明简单我却不会的题。

切入点当然是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不够深入了解吧。

posted @ 2017-10-29 15:24  莫名其妙的aaa  阅读(239)  评论(0编辑  收藏  举报