题目
n(n<=500)个点的完全图,
每次删掉一个点,直至删尽,
删一个则询问一次剩下每一对点的最短路权值之和。
思路来源
https://blog.youkuaiyun.com/Icefox_zhx/article/details/77967593
题解
离线+floyd
倒着做就是加点的过程,
考虑floyd,最外层floyd即枚举中间点k
那么我们加一个点,就把它用于全图更新
因为floyd对松弛点的顺序没有要求,
所以相当于加回的不是点,即所有点都在
而加点的时候,加的实际上是它的所有边
这样就可以给别的点带来更新了
想想学数据结构时候画的那个表,
还是要好好学课内吖
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=510;
int n;
ll dis[maxn][maxn],ans[maxn];
bool ok[maxn];//是否已经在点集中
int del[maxn];
int main()
{
scanf("%d",&n);
for(int i=0;i<n;++i)
{
for(int j=0;j<n;++j)
scanf("%I64d",&dis[i][j]);
}
for(int i=0;i<n;++i)
scanf("%d",&del[i]);
//假设点都在 加点是加所有边的过程 逆向加点
//枚举u为中间点,模拟floyd中最短路编号不超过k的过程
for(int k=n-1;k>=0;--k)
{
int u=del[k];
ok[--u]=1;
for(int i=0;i<n;++i)
{
for(int j=0;j<n;++j)
{
if(i==j)continue;
if(dis[i][u]+dis[u][j]<dis[i][j])dis[i][j]=dis[i][u]+dis[u][j];//i->j的路径只会出现这一次
//i和j都在当前图中,i->j的最短路应该被统计
//如果是当前图的路径就加上 否则就为以后做铺垫
if(ok[i]&&ok[j])ans[k]+=dis[i][j];
}
}
}
for(int i=0;i<n;++i)
printf("%I64d%c",ans[i],i==n-1?'\n':' ');
return 0;
}