参考博客:https://blog.csdn.net/qkoqhh/article/details/81301910
(这个大佬的代码压行实在太漂亮了…)
大致题意
给定一个无向图,有n个点(n<=50),m条边,q次询问(q<=100000),每次询问从x到y经至少k条边(k<=10000)的最短路。
思路
从经过k条边的最短路可以想到floyd的矩阵运算。但是由于最短路经过的条数和长度没有单调性…(坑点) 所以要多走几步看后面的是否更小,所以就走n不比较保险。如果用矩阵快速幂 复杂度qn^3logk ,据说直接被卡了(我是事后补题没验证过)由于询问过多,而本身问题范围比较小,于是考虑预处理分块。sqrt(k)作为一块,然后求答案的时候如果询问的k>块的大小,就枚举大块的终点和小块的起点,加一起。具体见代码吧…。其他整块和小于块的可以直接输出。这样整体的复杂度 sqrt(k)n^3+qn
代码
///分块做的时候需要注意边数和最短路并不严格单调,即多走几步可能会得到更优的解。。所以在对余数预处理的时候得多跑几条边,走多n条边可以说是很稳的了。。
#include<bits/stdc++.h>
using namespace std;
#define maxn 55
#define maxm 105
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
int t,n,m,q;
int d[160][maxn][maxn],f[maxm][maxn][maxn];
int main()
{
t=read();
//printf("t = %d\n",t);
while(t--){
n=read();m=read();
inc(i,1,n)inc(j,1,n)d[1][i][j]=INF;
int x,y,z;
inc(i,1,m){x=read();y=read();d[1][x][y]=min(d[1][x][y],read());}
inc(i,1,n)inc(j,1,n)d[0][i][j]=d[1][i][j];
inc(i,1,n)d[0][i][i]=0;
inc(p,2,150){
inc(i,1,n)inc(j,1,n)d[p][i][j]=INF;
inc(k,1,n)inc(i,1,n)inc(j,1,n)d[p][i][j]=min(d[p][i][j],d[p-1][i][k]+d[1][k][j]);
}
inc(i,1,n)inc(j,1,n)f[1][i][j]=d[100][i][j];
inc(p,2,100){
inc(i,1,n)inc(j,1,n)f[p][i][j]=INF;
inc(k,1,n)inc(i,1,n)inc(j,1,n)f[p][i][j]=min(f[p][i][j],f[p-1][i][k]+f[1][k][j]);
}
dec(p,149,0)inc(i,1,n)inc(j,1,n)d[p][i][j]=min(d[p][i][j],d[p+1][i][j]);
q=read();
while(q--){
x=read();y=read();z=read();
int ans=INF;
if(z>100)inc(i,1,n)ans=min(ans,f[z/100][x][i]+d[z%100][i][y]);
if(z<=100)ans=min(ans,d[z][x][y]);
if(z%100==0)ans=min(ans,f[z/100][x][y]);
if(ans<INF)printf("%d\n",ans);
else printf("-1\n");
}
}
return 0;
}