矩陣快速冪加速最短路

Ораичскевеета發表於2024-11-04

矩陣快速冪加速最短路

通常用來最佳化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;
}

相關文章