题目链接
思路:
floyd
f
l
o
y
d
的本质是不断加入中转点来更新两点间的最短路,要求不经过
x
x
点的最短路,也就是中转点没有,可以对
x
x
分治,当
[l,r]
[
l
,
r
]
的点都不经过的时候两点间的最短路,那么
solve(l,mid)
s
o
l
v
e
(
l
,
m
i
d
)
的时候,把
[mid+1,r]
[
m
i
d
+
1
,
r
]
的点作为中转点更新两点间最短路,同样
solve(mid+1,r)
s
o
l
v
e
(
m
i
d
+
1
,
r
)
的时候也是如此,状态恢复可以利用时间倒流,记住所有操作,逆着恢复状态即可。
#include<bits/stdc++.h>
typedef long long ll;
const int INF = 1e9 + 10;
const int maxn = 3e2 + 5;
using namespace std;
struct pa {
int x, y, dis;
pa(int x = 0, int y = 0, int dis = 0) : x(x), y(y), dis(dis) {}
};
int dis[maxn][maxn];
int n, m, can[maxn][maxn], cnt = 0;
pa rec[maxn * maxn * 100];
ll ans = 0;
void update(int v) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(dis[i][j] <= dis[i][v] + dis[v][j]) continue;
rec[cnt++] = pa(i, j, dis[i][j]);
dis[i][j] = dis[i][v] + dis[v][j];
}
}
}
void recover(int now) {
while(cnt > now) {
cnt--; pa p = rec[cnt];
dis[p.x][p.y] = p.dis;
}
}
void solve(int l, int r) {
if(l == r) {
for(int i = 1; i <= n; i++) {
if(i == l) continue;
for(int j = 1; j <= n; j++) {
if(j == l) continue;
if(dis[i][j] != INF) ans += dis[i][j];
else ans--;
}
}
return ;
}
int mid = (l + r) >> 1, res = cnt;
for(int i = mid + 1; i <= r; i++) update(i);
solve(l, mid); recover(res);
for(int i = l; i <= mid; i++) update(i);
solve(mid + 1, r); recover(res);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
scanf("%d", &dis[i][j]);
if(dis[i][j] == -1) dis[i][j] = INF;
}
}
solve(1, n);
printf("%lld\n", ans);
return 0;
}