题意:
给一张有向图,q个询问,询问s到t至少经过k条路的最近距离。
题解:
f[k][i][j]表示从i到j经过k条路的最短路,则f[k][i][j]=min{f[k-1][i][t]+map[t][j]},(1<=t<=n),这个方程很好理解,从i到j经过k条路,我们枚举中间点t,从i到t经过k-1条,从t到j经过1条,取最短的就是从i到j经过k条。
一般的,还有f[k][i][j]=min{f[k-x][i][t]+f[x][t][j]},我们将这种操作定义为f[k-x]的图与f[x]的图的合并。
现在,对于每一个询问,迭代合并k-1次即可以求解,但时间显然是无法接受的,这里考虑分块的思想。
路径的条数最多10000,我们将k拆分成100p+q的形式,其中 p、q都小于100,我们首先预处理出f[1],f[2]......f[100]的图,再预处理出f[100],f[200],f[300]......f[10000]的图,这样对于每个k,枚举中间的x,min{f[100p][s][x]+f[q][x][t]}就是从s到t恰好经过k条路的最短路。
题目要求是至少经过k条路,我们将 f[k][i][j]=min{f[k][i][j],f[k+1][i][j]},从后向前过一遍就是至少经过k条路的最短路了,这是显然的。
注意,当q接近100时,比如k=199,此时假设最短路要经过202条路径,我们这样是无法求解的,因为之前的条件时q<100,而正解的方案是 100(p=1)+102(q=99),我们意识到给出的k,正解一定是在[k,k+n]的范围内,因为k足够大时,正解的路径一定是在重复走一个环,而环的边数一定小于n,所以最多走k+n步。我们只需将f[101],f[102]...f[105]的图多求出来即可。
代码:
#include<bits/stdc++.h>
#define N 50
#define INF 1e9
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lowwer_bound
#define eps 1e-8
const LL P=1e9+7;
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using namespace std;
int n;
struct M{
int m[N][N];
M()
{
memset(this,0x3f,sizeof (M));
}
friend M operator * (M& a, M& b){
M c;
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
{
for (int k=0; k<n; k++)
c.m[i][k]=min(c.m[i][k],a.m[i][j]+b.m[j][k]);
}
return c;
}
};
int a[102][N][N],b[152][N][N];
int main()
{
IO
int T;
cin>>T;
while(T--)
{
int m,q,j,k,l; M f;
cin>>n>>m;
for (int i=1;i<=m;i++)
{
cin>>j>>k>>l; j--;k--;
f.m[j][k]=min(f.m[j][k],l);
}
M f2;
for (int i=0;i<n;i++) for (int j=0;j<n;j++) if (i==j) f2.m[i][j]=0;else f2.m[i][j]=INF;
for (int j=0;j<n;j++) for (int k=0;k<n;k++)
if (j==k) a[0][j][k]=b[0][j][k]=0;else a[0][j][k]=b[0][j][k]=INF;
for (int i=1;i<=150;i++)
{
f2=f2*f;
for (int j=0;j<n;j++)
for (int k=0;k<n;k++) b[i][j][k]=f2.m[j][k];
}
for (int i=0;i<n;i++)
for (int j=0;j<n;j++) f2.m[i][j]=b[100][i][j];
M f3;
for (int i=0;i<n;i++) for (int j=0;j<n;j++) if (i==j) f3.m[i][j]=0;else f3.m[i][j]=INF;
for (int i=1;i<=100;i++)
{
f3=f3*f2;
for (int j=0;j<n;j++)
for (int k=0;k<n;k++) a[i][j][k]=f3.m[j][k];
}
for (int i=149;~i;i--)
{
for (int j=0;j<n;j++)
for (int k=0;k<n;k++)
{
b[i][j][k]=min(b[i][j][k],b[i+1][j][k]);
if (i<100)a[i][j][k]=min(a[i][j][k],a[i+1][j][k]);
}
}
cin>>q;
while(q--)
{
int s,t,k;
cin>>s>>t>>k;
s--;t--;
int ss=k/100,tt=k%100; int ans=INF;
for (int i=0;i<n;i++)
{
ans=min(ans,a[ss][s][i]+b[tt][i][t]);
}
if (ans!=INF)cout<<ans<<endl;else cout<<"-1\n";
}
}
return 0;
}