题目
题目链接:http://noip.ybtoj.com.cn/problem/20078
思路
考虑 Floyd 算法的实质:(f[k][i][j]) 表示只经过 (1sim k) 的点时,(i) 到 (j) 的最短路。
发现第一维的枚举顺序其实可以是任意的。所以考虑分治。
对于目前分治到的区间 ([l,r]),我们先计算 ([l,mid]) 的贡献,继续分治 ((mid,r]);清空贡献后计算 ((mid,r]) 的贡献,分治计算 ([l,mid])。
当 (l=r) 时,除了 (l) 以外的所有点贡献都计算过了。直接枚举端点计算答案即可。
时间复杂度 (O(n^3log n))。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=330,LG=10,Inf=1e9;
int n,dis[LG][N][N];
ll ans;
void solve(int l,int r,int dep)
{
if (l==r)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=l && j!=l)
ans+=(dis[dep][i][j]<Inf ? dis[dep][i][j] : -1);
return;
}
int mid=(l+r)>>1;
memcpy(dis[dep+1],dis[dep],sizeof(dis[dep]));
for (int k=l;k<=mid;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j && j!=k && i!=k)
dis[dep+1][i][j]=min(dis[dep+1][i][j],dis[dep+1][i][k]+dis[dep+1][k][j]);
solve(mid+1,r,dep+1);
memcpy(dis[dep+1],dis[dep],sizeof(dis[dep]));
for (int k=mid+1;k<=r;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j && j!=k && i!=k)
dis[dep+1][i][j]=min(dis[dep+1][i][j],dis[dep+1][i][k]+dis[dep+1][k][j]);
solve(l,mid,dep+1);
}
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
scanf("%d",&dis[0][i][j]);
if (dis[0][i][j]==-1) dis[0][i][j]=Inf;
}
solve(1,n,0);
printf("%lld",ans);
return 0;
}