矩陣快速冪加速最短路
通常用來最佳化Floyd的實現
[NOI Online #1 入門組] 魔法
題目描述
C 國由 $n$ 座城市與 $m$ 條有向道路組成,城市與道路都從 $1$ 開始編號,經過 $i$ 號道路需要 $t_i$ 的費用。
現在你要從 $1$ 號城市出發去 $n$ 號城市,你可以施展最多 $k$ 次魔法,使得透過下一條道路時,需要的費用變為原來的相反數,即費用從 $t_i$ 變為 $-t_i$。請你算一算,你至少要花費多少費用才能完成這次旅程。
注意:使用魔法只是改變一次的花費,而不改變一條道路自身的 $t_i$;最終的費用可以為負,並且一個城市可以經過多次(包括 $n$ 號城市)。
輸入格式
輸入的第一行有三個整數,分別代表城市數 $n$,道路數 $m$ 和魔法次數限制 $k$。
第 $2$ 到第 $(m + 1)$ 行,每行三個整數。第 $(i + 1)$ 行的整數 $u_i, v_i, t_i$ 表示存在一條從 $u_i$ 到 $v_i$ 的單向道路,經過它需要花費 $t_i$。
輸出格式
輸出一行一個整數表示最小的花費。
樣例 #1
樣例輸入 #1
4 3 2
1 2 5
2 3 4
3 4 1
樣例輸出 #1
-8
樣例 #2
樣例輸入 #2
2 2 2
1 2 10
2 1 1
樣例輸出 #2
-19
提示
輸入輸出樣例 1 解釋
依次經過 $1$ 號道路、$2$ 號道路、$3$ 號道路,並在經過 $1,2$ 號道路前使用魔法。
輸入輸出樣例 2 解釋
依次經過 $1$ 號道路、$2$ 號道路、$1$ 號道路,並在兩次經過 $1$ 號道路前都使用魔法。
資料規模與約定
本題共 $20$ 個測試點,各測試點資訊見下表。
測試點編號 | $n \leq$ | $m \leq$ | $k \leq$ | 無環 |
---|---|---|---|---|
$1 \sim 2$ | $5$ | $20$ | $0$ | 不保證 |
$3 \sim 4$ | $10$ | $20$ | $50$ | 不保證 |
$5 \sim 6$ | $10$ | $20$ | $0$ | 不保證 |
$7 \sim 8$ | $20$ | $200$ | $50$ | 是 |
$9 \sim 10$ | $20$ | $200$ | $0$ | 不保證 |
$11 \sim 12$ | $100$ | $200$ | $50$ | 是 |
$13 \sim 14$ | $100$ | $200$ | $50$ | 不保證 |
$15 \sim 18$ | $100$ | $2500$ | $1000$ | 不保證 |
$19 \sim 20$ | $100$ | $2500$ | $10^6$ | 不保證 |
對於【無環】一欄為“是”的測試點,保證給出的圖是一張有向無環圖,否則不對圖的形態做任何保證。
對於全部的測試點,保證:
- $1 \leq n \leq 100$,$1 \leq m \leq 2500$,$0 \leq k \leq 10^6$。
- $1 \leq u_i, v_i \leq n$,$1 \leq t_i \leq 10^9$。
- 給出的圖無重邊和自環,且至少存在一條能從 $1$ 到達 $n$ 的路徑。
首先有70分的做法,即使用動態規劃。
將邊存入陣列中,$g[i].u$和$g[i].v$表示邊的兩端,g[i].w表示邊權
設$dp[i][j][k]$表示從i到j使用k次魔法時的最短路。
當k=0時:$dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+dp[k][j][0])$
當k=1時:$dp[i][j][1]=min(dp[i][j][1],dp[i][g[k].u][0]+dp[g[k].v][j][0]-g[k].w)$
使用k次魔法:$dp[i][j][k]=min(dp[i][j][k],dp[i][l][k-1]+dp[l][j][1])$
$k-1+1$剛好為k,表示魔法的使用次數。
最後輸出$dp[1][n][k]$即為答案。
但是這個做法的時間複雜度為$O(n3k)$,空間複雜度為$O(n2k)$,無法AC。
這裡就需要用一個工具——矩陣加速來降低複雜度了。
先對處理樣例1時dp陣列進行打表:
k=0時:
0 5 9 10
\ 0 4 5
\ \ 0 1
\ \ \ 0
k=1時:
\ -5 -1 0
\ 0 -4 -3
\ \ 0 -1
\ \ \ 0
k=2時:
0 -5 -9 -8
\ 0 -4 -5
\ \ 0 -1
\ \ \ 0
按照題目要求,可以把矩陣乘法進行修改:
$\begin{bmatrix}a_{1,1}&a_{1,2}&a_{1,3}&a_{1,4}\a_{2,1}&a_{2,2}&a_{2,3}&a_{2,4}\a_{3,1}&a_{3,2}&a_{3,3}&a_{3,4}\a_{4,1}&a_{4,2}&a_{4,3}&a_{4,4}\\end{bmatrix} *\begin{bmatrix}b_{1,1}&b_{1,2}&b_{1,3}&b_{1,4}\b_{2,1}&b_{2,2}&b_{2,3}&b_{2,4}\b_{3,1}&b_{3,2}&b_{3,3}&b_{3,4}\b_{4,1}&b_{4,2}&b_{4,3}&b_{4,4}\\end{bmatrix}=\begin{bmatrix}min(a_{1,k},b_{k,1})&min(a_{1,k},b_{k,2})&min(a_{1,k},b_{k,3})&min(a_{1,k},b_{k,4})\min(a_{2,k},b_{k,1})&min(a_{2,k},b_{k,2})&min(a_{2,k},b_{k,3})&min(a_{2,k},b_{k,4})\min(a_{3,k},b_{k,1})&min(a_{3,k},b_{k,2})&min(a_{3,k},b_{k,3})&min(a_{3,k},b_{k,4})\min(a_{4,k},b_{k,1})&min(a_{4,k},b_{k,2})&min(a_{4,k},b_{k,3})&min(a_{4,k},b_{k,4})\\end{bmatrix}$
驗證後可得到
另初始矩陣為$E_{1}$,第二個矩陣為$E_{2}$,可得到$E_{k}=E_{1}*E_{2}^k$。
$E_{1}$和$E_{2}$可用動態規劃(Floyd) 直接求得,然後在用ksm即可快速得到答案。
時間複雜度為$O(n2m+n3log(k))$,空間複雜度為$O(n^2)$,完全可透過此題。
#include<bits/stdc++.h>
#define inf 1000000000000000
using namespace std;
const int N=110;
struct Matrix{ //矩陣
long long a[N][N];
int n,m;
Matrix(int n=0,int m=0):n(n),m(m){}
void init(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) a[i][j]=0;
else a[i][j]=inf;
}
}
}
Matrix operator*(const Matrix&A) const{
Matrix ret(n,A.n);
for(int i=1;i<=n;i++){
for(int j=1;j<=A.m;j++){
long long tmp=inf;
for(int k=1;k<=m;k++) tmp=min(tmp,a[i][k]+A.a[k][j]);
ret.a[i][j]=tmp;
}
}
return ret;
}
};
Matrix ksm(Matrix A,int b){ //快速冪
Matrix ret(A.n,A.n);
ret.init();
while(b){
if(b&1) ret=ret*A;
b>>=1;
A=A*A;
}
return ret;
}
int main(){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
Matrix D(n,n),K(n,n);
D.init();
K.init();
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
D.a[u][v]=w;
K.a[u][v]=-w;
}
Matrix G=ksm(D,n-1);
G=G*ksm(K*G,k);
printf("%lld",G.a[1][n]);
return 0;
}