【YbtOJ#20078】路径之和
题目
题目链接:http://noip.ybtoj.com.cn/problem/20078
思路
考虑 Floyd 算法的实质:\(f[k][i][j]\) 表示只经过 \(1\sim k\) 的点时,\(i\) 到 \(j\) 的最短路。
发现第一维的枚举顺序其实可以是任意的。所以考虑分治。
对于目前分治到的区间 \([l,r]\),我们先计算 \([l,mid]\) 的贡献,继续分治 \((mid,r]\);清空贡献后计算 \((mid,r]\) 的贡献,分治计算 \([l,mid]\)。
当 \(l=r\) 时,除了 \(l\) 以外的所有点贡献都计算过了。直接枚举端点计算答案即可。
时间复杂度 \(O(n^3\log 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;
}