题目
思路
动态规划:
状态定义:
f
[
k
]
[
i
]
[
j
]
对应使用了不超过
k
次魔法,从
i
到
j
的路径集合
f[k][i][j] 对应使用了不超过k次魔法,从i到j的路径集合
f[k][i][j]对应使用了不超过k次魔法,从i到j的路径集合
状态表示:
路径长度最小值
路径长度最小值
路径长度最小值
目标状态:
f
[
k
]
[
1
]
[
n
]
f[k][1][n]
f[k][1][n]
状态转移:
f
[
k
]
[
i
]
[
j
]
=
m
i
n
(
f
[
k
−
1
]
[
i
]
[
t
]
+
f
[
1
]
[
t
]
[
j
]
)
,
t
∈
[
1
,
n
]
f[k][i][j] = min(f[k-1][i][t]+f[1][t][j]), t \in [1,n]
f[k][i][j]=min(f[k−1][i][t]+f[1][t][j]),t∈[1,n]
优化:快速幂:
将一个
k
值对应一个矩阵,代表
k
代矩阵。我们就可以将状态转移方程转化为:
将一个k值对应一个矩阵,代表k代矩阵。我们就可以将状态转移方程转化为:
将一个k值对应一个矩阵,代表k代矩阵。我们就可以将状态转移方程转化为:
f
k
[
i
]
[
j
]
=
m
i
n
(
f
k
−
1
[
i
]
[
t
]
,
f
1
[
t
]
[
j
]
)
,
t
∈
[
1
,
n
]
\begin{align*} f_{k}[i][j] = min(f_{k-1}[i][t], f_{1}[t][j]), t \in [1,n] \end{align*}
fk[i][j]=min(fk−1[i][t],f1[t][j]),t∈[1,n]
即该方程给出了k代矩阵的一个元素的计算方式。一个k代矩阵可以依靠一个k-1代矩阵和一个1代矩阵计算出来,类似地:
f
k
=
f
k
−
1
f
1
f
k
=
(
f
k
−
2
f
1
)
(
f
1
f
0
)
f
k
=
f
1
k
f
0
f
7
=
f
4
(
f
2
(
f
1
f
0
)
)
\begin{align*} &f_{k} = f_{k-1}f_{1}\\ &f_{k} = (f_{k-2}f_{1})(f_{1}f_{0})\\ &f_{k} = f_{1}^{k}f_{0}\\ &f_{7} = f_{4}(f_{2}(f_{1}f_{0}))\\ \end{align*}
fk=fk−1f1fk=(fk−2f1)(f1f0)fk=f1kf0f7=f4(f2(f1f0))
矩阵间的运算满足结合律,对k重运算可应用快速幂算法
f0就是对于邻接矩阵进行floyd后的矩阵d
枚举所有边进行一次魔法,尝试更新所有f0的路径,使得所有路径都最多用过一次魔法,得到f1
复杂度分析:快速幂复杂度*单个矩阵状态数量*每次状态转移的操作次数:
O
(
l
o
g
K
⋅
(
n
3
)
)
\begin{align*} O(logK \cdot (n^{3})) \end{align*}
O(logK⋅(n3))
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 110, M = 2510;
struct edge{
int a;
int b;
int c;
} edge[M];
LL d[N][N], f[N][N];
int n, m, K;
void mul(LL c[][N], LL a[][N], LL b[][N])
{
LL temp[N][N];
memset(temp, 0x3f, sizeof temp);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
for(int k = 1; k <= n; k++)
{
temp[i][j] = min(temp[i][j], a[i][k] + b[k][j]);
}
}
}
memcpy(c, temp, sizeof temp);
}
LL qmi()
{
while(K)
{
if(K&1) mul(d, d, f);
mul(f, f, f);
K >>= 1;
}
return d[1][n];
}
int main()
{
cin >> n >> m >> K;
memset(d, 0x3f, sizeof d);
for (int i = 1; i <= n; i ++ ) d[i][i] = 0;
for(int i = 1; i <= m; i++)
{
int a, b, c;
cin >> a >> b >> c;
edge[i] = {a, b, c};
d[a][b] = c;
}
for(int k = 1; k <= n; k++)
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
memcpy(f, d, sizeof f);
for(int k = 1; k <= m; k++)
{
int a = edge[k].a, b = edge[k].b, c = edge[k].c;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
f[i][j] = min(f[i][j], d[i][a] - c + d[b][j]);
}
}
}
cout << qmi();
return 0;
}