題目大意
給定 n點m邊帶權有向圖,從 1 到 \(n\) 的路徑中,經過一條邊時可讓其權值變為相反數,再變為原權值,求路徑最小權值。
分析
先用 \(Floyd\) 求出全源最短路。
借用 \(Floyd\) 陣列列出 \(dp\) 狀態,\(f_{i,j}\) 表示從 \(i\) 到 \(j\) 的最短路權值。
但似乎進行不下去了,我們不妨將邊反轉的數量加入 \(dp\) 狀態中。
狀態變為 \(f_{k,i,j}\) 表示用了 從 \(i\) 到 \(j\) 的路徑上反轉了 \(k\) 次的權值。
發現轉移方程大概可以寫成這樣 $$dp_{i,x,k_1}+dp_{y,j,k_2}-w(x,y) \rightarrow dp_{i,j,k_1+k_2+1}$$
發現這十分類似與快速冪的形式!而且 \(max,+\) 具有結合律。
我們成功找到解題關鍵,關於圖論題目可以用矩陣快速冪解決。
現在問題轉化為了如何構造矩陣。
考慮在求全源最短路的陣列上,發現基礎矩陣即為 \(f_{1,i,j}\)!可直接 \(O(mn^2)\)求出,接下來進行廣義矩陣快速冪即可,複雜度為 \(O(n^3logk)\),
可以透過此題。
程式碼
const int N=101,M=2501;
int n,m,k;
int u[M],v[M],w[M];
struct node{
int a[N][N];
node(){memset(a,0x3f,sizeof(a));}
friend node operator *(node x,node y){
node z;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
z.a[i][j]=min(z.a[i][j],x.a[i][k]+y.a[k][j]);
return z;
}
}a,b;
node power(node x,int y){
node ans=a;
while(y){
if(y&1) ans=ans*x;
x=x*x;y>>=1;
}
return ans;
}
signed main(){
n=rd(),m=rd(),k=rd();
for(int i=1;i<=m;i++){
u[i]=rd(),v[i]=rd(),w[i]=rd();
a.a[u[i]][v[i]]=w[i];
}
for(int i=0;i<=n;i++) a.a[i][i]=0;
for(int t=1;t<=n;t++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a.a[i][j]=min(a.a[i][j],a.a[i][t]+a.a[t][j]);
if(!k) return cout<<a.a[1][n],0;
for(int t=1;t<=m;t++){
int val=w[t];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
b.a[i][j]=min(b.a[i][j],min(a.a[i][j],a.a[i][u[t]]+a.a[v[t]][j]-val));
}
}
}
cout<<power(b,k).a[1][n];
return 0;
}