Walking Plan HDU - 6331 floyd+矩阵+分块

参考博客: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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值