最短路之和

最短路之和

给定一个 n 个点的加权有向图,点的编号为 1n

图中任意两点之间都有两条方向相反的边连接两点。

从点 i 到点 j 的边的长度为 aij

给定一个 1n 的排列 x1,x2,,xn

你需要对该图依次进行 n 个操作。

其中,第 i 个操作是将点 xi 以及该点的所有入边和出边从图中移除。

在执行每一个操作之前,你还需要进行如下计算:

  • 对于每个当前剩余点对 (u,v),计算从点 u 到点 v 的最短路长度。
  • 将这些最短路长度全部相加得到最短路之和。

注意:

  • 剩余点对 (u,v):两个还未被移除的点 u,vuv)组成的点对。
  • (u,v)(v,u) 是两个不同点对,需分别计算最短路长度并计入最短路之和。
  • n 个操作是按顺序依次执行的,前面的操作会对后面的计算产生影响。

请你输出执行每个操作前计算得到的最短路之和。

输入格式

第一行包含整数 n

接下来 n 行,每行包含 n 个整数,其中第 i 行第 j 个数为 aij

最后一行包含 n 个整数 x1,x2,,xn

输出格式

共一行,输出 n 个整数,其中第 i 个整数表示执行第 i 个操作之前,计算得到的最短路之和。

数据范围

4 个测试点满足 1n4
所有测试点满足 1n5001aij105aii=01xin,保证 x1xn 是一个 1n 的排列。

输入样例1:

1
0
1

输出样例1:

0

输入样例2:

2
0 5
4 0
1 2

输出样例2:

9 0

输入样例3:

4
0 3 1 1
6 0 400 1
2 4 0 1
1 1 1 0
4 1 2 3

输出样例3:

17 23 404 0

 

解题思路

  比赛的时候猜对了做法,这题本质就是考对Floyd算法原理的理解。

  因为这题要求任意两点间的最短路径,所以首先容易想到用Floyd。然后就是每次从点集中删除一个点,求点集中剩余点的任意两点间的最短距离的总和。删点的话不好做,那么试试倒过来做行不行。就是一开始点集为空,然后每次往点集中加入一个点(以及对应的边),再求点集中任意两点最短路。如果了解Floyd算法的原理的话那么就会知道,当第一层循环枚举完k,此时求得的是任意两点间只经过节点编号1k的最短距离。因此每次我们当我们枚举到k,那么就可以求出任意两点间只经过前k个点的最短距离,而前k个点就是我们往点集中加的前k个点。

  下面补个Floyd的推导过程,其实本质就是个dp。

  定义状态f(k,i,j)表示所有从i出发,最终走到j,且中间只经过节点编号不超过k的所有路径,属性就是路径长度的最小值。根据ij的路径中(不包含起点i和终点j)是否包含节点k来划分集合。

  1. 如果ij的路径不包含节点k,那么意味着当前ij的所有路径中只含有节点1k1,对应的集合就是f(k1,i,j)
  2. 如果如果ij的路径包含节点k,那么有ikj,其中ikkj的路径中只含有节点1k1,分别对应的集合为f(k1,i,k)f(k1,k,j)

  对这两种情况取最小值,那么就有状态转移方程f(k,i,j)=min{f(k1,i,j), f(k1,i,k)+f(k1,k,j)}

  然后就是证明把状态定义的第一维k去掉得到的f(i,j)与原本的状态是等价的。

  当j=k时,原本有f(k,i,k)=min{f(k1,i,k), f(k1,i,k)+f(k1,k,k)},由于f(k1,k,k)=0(初始化的时候有f(0,k,k)=0,即k到本身的最小距离为0),因此得到f(k,i,k)=min{f(k1,i,k), f(k1,i,k)}=f(k1,i,k)。因此对于j=k的情况去掉第一维不会影响结果,因为状态f(i,k)存的是上一层的f(i,k)的结果。

  同理当i=k时,原本有f(k,k,j)=min{f(k1,k,j), f(k1,k,k)+f(k1,k,j)},由于f(k1,k,k)=0,因此得到f(k,k,j)=min{f(k1,k,j), f(k1,k,j)}=f(k1,k,j)。因此对于i=k的情况去掉第一维不会影响结果,因为状态f(k,j)存的是上一层的f(k,j)的结果。

  对于一般的f(k,i,j),有f(k,i,j)=min{f(k1,i,j), f(k1,i,k)+f(k1,k,j)}=min{f(k1,i,j), f(k,i,k)+f(k,k,j)}

  因此去掉第一维可以发现f(i,j)=min{f(i,j), f(i,k)+f(k,j)},与之前的状态转移方程是等价的。

  因此状态转移方程就可以写成f(i,j)=min{f(i,j), f(i,k)+f(k,j)}

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 510;
 7 
 8 LL g[N][N];
 9 int p[N];
10 LL ans[N];
11 
12 int main() {
13     int n;
14     scanf("%d", &n);
15     for (int i = 1; i <= n; i++) {
16         for (int j = 1; j <= n; j++) {
17             scanf("%lld", &g[i][j]);
18         }
19     }
20     for (int i = 1; i <= n; i++) {
21         scanf("%d", p + i);
22     }
23     for (int k = n; k; k--) {   // 倒序插入点,求只经过p[k]~p[n]的最短路径
24         for (int i = 1; i <= n; i++) {
25             for (int j = 1; j <= n; j++) {
26                 g[i][j] = min(g[i][j], g[i][p[k]] + g[p[k]][j]);
27             }
28         }
29         for (int i = n; i >= k; i--) {  // 只统计已加入的点的结果
30             for (int j = n; j >= k; j--) {
31                 ans[k] += g[p[i]][p[j]];
32             }
33         }
34     }
35     for (int i = 1; i <= n; i++) {
36         printf("%lld ", ans[i]);
37     }
38     
39     return 0;
40 }
复制代码

 

参考资料

  AcWing 4872. 最短路之和(AcWing杯 - 周赛):https://www.acwing.com/video/4653/

  3.2 floyd算法及其扩展应用:https://www.acwing.com/file_system/file/content/whole/index/content/147349/

posted @   onlyblues  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-03-12 动态规划部分题集解
Web Analytics
点击右上角即可分享
微信分享提示