min 与 + 运算转换成类似于矩阵乘法的推导过程

  记录下由 min+ 运算转换成类似于矩阵乘法的推导过程,有错误请在评论区指出 qwq。

  我们先简单证明一下矩阵乘法的结合律。设有矩阵 An×mBm×pCp×q,要证明 (AB)C=A(BC)。等价于证 ((AB)C)i,j=(A(BC))i,j,其中 Ai,j 表示矩阵 A 的第 i 行第 j 列的元素。

((AB)C)i,j=u=1p(AB)i,u×Cu,j=u=1p(k=1mAi,k×Bk,u)×Cu,j=u=1p(k=1mAi,k×Bk,u×Cu,j)=k=1m(u=1pAi,k×Bk,u×Cu,j)=k=1mAi,k×(u=1pBk,u×Cu,j)=k=1mAi,k×(BC)k,j=(A(BC))i,j

  因此根据矩阵乘法的结合律,对于 AAn 可以写成 An,并假设 n=2bk+2bk1++2b0,其中 k=logxi[0,k], bi{0,1},利用结合律我们就可以通过 O(logn) 的复杂度来计算得到

An=A2bk+2bk1++2b0=A2bk×A2bk1××A2b0

  其中上面的证明过程用到的运算包括 +× 以及对应的交换律、结合律和分配律。对于矩阵乘法

(AB)i,j=k=1mAi,k×Bk,j

  为了更一般化,我们用运算符 来代替上面的加法与乘法,得到:

(AB)i,j=k=1mAi,k×Bk,j

  其中 满足交换律:

ab=baab=ba

  结合律:

(ab)c=a(bc)(ab)c=a(bc)

   的分配律:

a(bc)=(ab)(ac)

  很明显矩阵乘法中的加法 +× 就分别对应于 。同时发现把 min 代入 + 代入 ,也满足上面定义的一般形式,只需验证这三个性质均成立即可。

  交换律:

min{a,b}=min{b,a}a+b=b+a

  结合律:

min{min{a,b},c}=min{a,min{b,c}}(a+b)+c=a+(b+c)

  +min 的分配律:

a+min{b,c}=min{a+b,a+c}

  因此有

(AB)i,j=min1km{Ai,k+Bk,j}

  因此对于由 min+ 所构成的运算是具有结合律的,因此对于 AAn 同样可以写成 An,并且有

An=A2bk+2bk1++2b0=A2bk×A2bk1××A2b0

  注意这里的 A×B 是指两个矩阵间的运算,不是一般意义的矩阵乘法。对于 min+

(A×B)i,j=min1km{Ai,k+Bk,j}

  而一般意义的矩阵乘法是

(A×B)i,j=k=1mAi,j×Bj,k

  我们通过 Floyd求最短路 这道题目对这个性质进行运用。

  这里我们不用传统 Floyd 的 dp 状态定义,而是定义状态 f(k,i,j) 表示从 ij 且经过不超过 k 条边的所有路径所构成的集合中最短的路径长度。

  对于任意两点 (v,w) 间的距离,答案就是 f(n1,v,w),这是因为题目保证没有负环,因此任意两点间的最短路径不会超过 n1 条边。

  根据从 ij 的路径中所经过的点 u 来把路径分成两段,其中前面一段 iu 经过不超过 k1 条边,后面一段 uj 经过不超过 1 条边。这样就得到状态转移方程:

f(k,i,j)=min1un{f(k1,i,u)+f(1,u,j)}

  这种做法的时间复杂度为 O(n4)。这个时候就可以利用上面的性质来进行优化了。

  我们把 f(k,i,j) 看成是矩阵 Fn×nki 行第 j 的元素,因此上面的状态转移方程就变成了

Fi,jk=min1un{Fi,uk1+Fu,j1}

  矩阵间的运算就是 Fk=Fk1 F1,通过递推可以发现有 Fk=(F1)k,而我们最终要求的矩阵就是 Fn1=(F1)n1

  这里的 Fk 是一个 n×n 的矩阵,里面的每一个元素 Fi,jk 表示从 ij 经过不超过 k 条边的最短路径。

  这里就可以用快速幂来求 F1n1 次方来得到 Fn1,要注意是由 min+ 所构成的运算规则。

  同时根据定义知道矩阵 F0 中任意一个元素 Fi,j0 表示 ij 不经过任何边的最短路径,因此有

F0=[000]

  Fn1=F0 (F1)n1

  这样时间复杂度就降到了 O(n3logn),虽然还是不如传统的 Floyd 算法,但这种做法给了我们很多启示,比如魔法智乃酱的双塔问题·极(带修改的DP,DDP)Cow Relays 都是利用这种思想实现的。

  O(n3logn) 做法的 AC 代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 210, INF = 0x3f3f3f3f;
 5 
 6 int n, m, k;
 7 int f[N][N], g[N][N], tmp[N][N];
 8 
 9 void mul(int c[][N], int a[][N], int b[][N]) {
10     memset(tmp, 0x3f, sizeof(tmp));
11     for (int i = 1; i <= n; i++) {
12         for (int j = 1; j <= n; j++) {
13             for (int k = 1; k <= n; k++) {
14                 // 类似于矩阵乘法的c[i][j] = sum(a[i][k] * b[k][j])
15                 // 对于min和+运算有c[i][j] = min{a[i][k] + b[k][j]}
16                 tmp[i][j] = min(tmp[i][j], a[i][k] + b[k][j]);
17             }
18         }
19     }
20     memcpy(c, tmp, sizeof(tmp));
21 }
22 
23 int main() {
24     scanf("%d %d %d", &n, &m, &k);
25     // 一开始g=F^0
26     for (int i = 1; i <= n; i++) {
27         for (int j = 1; j <= n; j++) {
28             f[i][j] = g[i][j] = INF;
29         }
30         f[i][i] = g[i][i] = 0;
31     }
32     // 一开始f=F^1
33     while (m--) {
34         int v, w, wt;
35         scanf("%d %d %d", &v, &w, &wt);
36         f[v][w] = min(f[v][w], wt);
37     }
38     m = n - 1;  // 快速幂求F^{n-1}
39     while (m) {
40         if (m & 1) mul(g, g, f);
41         mul(f, f, f);
42         m >>= 1;
43     }
44     // 这时有g=F^{n-1}
45     while (k--) {
46         int v, w;
47         scanf("%d %d", &v, &w);
48         if (g[v][w] > INF >> 1) printf("impossible\n");
49         else printf("%d\n", g[v][w]);
50     }
51     
52     return 0;
53 }
复制代码

 

参考资料

  矩阵乘法笔记:https://www.cnblogs.com/wangruidong03/p/15891893.html

posted @   onlyblues  阅读(422)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
历史上的今天:
2022-03-30 翻转树边
2021-03-30 是否同一棵二叉搜索树
Web Analytics
点击右上角即可分享
微信分享提示