题目大意
给定 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;
}
标签:node,洛谷,P6190,int,矩阵,rd,权值,dp
From: https://www.cnblogs.com/smilemask/p/18303969