魔法[NOI Online #1 入门组][最短路][矩阵乘法]

文章目录

题目

Luogu
1 − > n 1->n 1>n 最短路,可以多次经过终点,有 k k k 次边权取反操作,求最短路

思路

f i j f_{ij} fij: i , j i,j i,j 的最短路
g i j g_{ij} gij: i , j i,j i,j 用至多一次取反最短路
可以发现的是 g g g 数组当 i ≠ j i\not=j i=j 时显然会用一次取反
那么定义 h u k h_{uk} huk 表示 1 − > u 1->u 1>u 用至多 k k k 次魔法的最短路
那么 h u , 0 = f 1 , u h_{u,0}=f_{1,u} hu,0=f1,u f u , 1 = g 1 , u f_{u,1}=g_{1,u} fu,1=g1,u
转移如下:
h u , k = m i n { h v , k − 1 + g v , u } h_{u,k}=min\{h_{v,k-1}+g_{v,u}\} hu,k=min{hv,k1+gv,u}
可以写成矩阵
( h 1 , k , h 2 , k , . . . , h n k ) ∗ ( g 11 , g 12 , . . . , g 1 n g 21 , g 22 , . . . , g 2 n . . . g n 1 , g n 2 , . . . , g n n ) = ( h 1 , k + 1 , h 2 , k + 1 , . . . , h n k + 1 ) \left( \begin{matrix} h_{1,k},h_{2,k},...,h_{nk} \end{matrix} \right) * \left( \begin{matrix} g_{11},g_{12},...,g_{1n}\\ g_{21},g_{22},...,g_{2n}\\ ...\\ g_{n1},g_{n2},...,g_{nn} \end{matrix} \right)= \left( \begin{matrix} h_{1,k+1},h_{2,k+1},...,h_{nk+1} \end{matrix} \right) (h1,k,h2,k,...,hnk)g11,g12,...,g1ng21,g22,...,g2n...gn1,gn2,...,gnn=(h1,k+1,h2,k+1,...,hnk+1)
记转移矩阵为 G G G 初始矩阵为 H H H 那么 H ′ = H ∗ G k H'=H*G^{k} H=HGk
类比一般的矩阵乘法 有
a ∗ ( b + c ) = a ∗ b + a ∗ c a*(b+c)=a*b+a*c a(b+c)=ab+ac
a + m i n ( b , c ) = m i n ( a + b , b + c ) a+min(b,c)=min(a+b,b+c) a+min(b,c)=min(a+b,b+c)
仍然有矩阵乘法的结合律
可以再推导一次 ( a ∗ b ) × ( b ∗ c ) × ( c ∗ d ) (a*b)\times(b*c)\times(c*d) (ab)×(bc)×(cd)
p = f ∗ g , q = g ∗ h , s = f ∗ g ∗ h p=f*g,q=g*h,s=f*g*h p=fgq=ghs=fgh

s i j = p ∗ h = m i n k = 1 c { p i k + h k j } = m i n k = 1 c { m i n l = 1 b { f i l + g l k } + h k j } = m i n k = 1 c { m i n l = 1 b { f i l + g l k + h k j } } = m i n l = 1 b { m i n k = 1 c { f i l + g l k + h k j } } = m i n l = 1 b { f i l + m i n k = 1 c { g l k + h k j } } = m i n k = 1 c { f i l + q l j } = f ∗ q s_{ij}=p*h=min_{k=1}^{c}\{p_{ik}+h_{kj}\} \\=min_{k=1}^{c}\{min_{l=1}^b\{f_{il}+g_{lk}\}+h_{kj}\} \\=min_{k=1}^{c}\{min_{l=1}^b\{f_{il}+g_{lk}+h_{kj}\}\} \\=min_{l=1}^b\{min_{k=1}^{c}\{f_{il}+g_{lk}+h_{kj}\}\} \\=min_{l=1}^b\{f_{il}+min_{k=1}^{c}\{g_{lk}+h_{kj}\}\} \\=min_{k=1}^{c}\{f_{il}+q_{lj}\}=f*q sij=ph=mink=1c{pik+hkj}=mink=1c{minl=1b{fil+glk}+hkj}=mink=1c{minl=1b{fil+glk+hkj}}=minl=1b{mink=1c{fil+glk+hkj}}=minl=1b{fil+mink=1c{glk+hkj}}=mink=1c{fil+qlj}=fq

代码

#include<set>    
#include<map>    
#include<stack>    
#include<ctime>    
#include<cstdio>    
#include<queue>    
#include<cmath>    
#include<vector>    
#include<cstring>   
#include<climits>    
#include<iostream>   
#include<algorithm>
using namespace std;
#define LL long long
int read(){
    int f=1,x=0;char c=getchar();
    while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
    while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
    return f*x;
}
#define MAXN 100
#define MAXM 2500
#define INF 10000000000000000ll
struct Matrix{
	int r,c;
	LL m[MAXN+5][MAXN+5];
	void Init(int R,int C){
		r=R,c=C;
		for(int i=1;i<=r;i++)
			for(int j=1;j<=c;j++)
				m[i][j]=INF;
		return ;
	}
	friend Matrix operator * (Matrix A,Matrix B){
		Matrix C;
		C.Init(A.r,B.c);
		for(int i=1;i<=A.r;i++)
			for(int j=1;j<=B.c;j++)
				for(int k=1;k<=A.c;k++)
					C.m[i][j]=min(C.m[i][j],A.m[i][k]+B.m[k][j]);
		return C;
	}
}A,B,C;
Matrix Pow(Matrix x,int y){
	Matrix ret;
	ret.Init(x.r,x.c);
	for(int i=1;i<=x.r;i++)
		ret.m[i][i]=0;
	while(y){
		if(y&1) ret=ret*x;
		x=x*x,y>>=1;
	}
	return ret;
}
LL f[MAXN+5][MAXN+5],g[MAXN+5][MAXN+5];
void Floyd(int n){
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
	return ;
}
struct Edge{
	int u,v,w;	
}edge[MAXM+5];
int main(){
	int n=read(),m=read(),k=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i!=j)
				f[i][j]=g[i][j]=INF;
			else f[i][i]=0,g[i][i]=INF;
	for(int i=1;i<=m;i++){
		edge[i].u=read(),edge[i].v=read(),edge[i].w=read();
		f[edge[i].u][edge[i].v]=edge[i].w;
	}
	Floyd(n);
	for(int t=1;t<=m;t++){
		int u=edge[t].u,v=edge[t].v;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				g[i][j]=min(g[i][j],f[i][u]-edge[t].w+f[v][j]);
	}
	A.Init(1,n),B.Init(n,n);
	for(int j=1;j<=n;j++)
		A.m[1][j]=f[1][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			B.m[i][j]=g[i][j];
	C=A*Pow(B,k);
	printf("%lld\n",C.m[1][n]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值