hdu6331 Problem M. Walking Plan

传送门

题目大意

给你一个n点m条边的有向图,q次询问,给定s,t,k,求由s到t至少经过k条边的最短路。

分析

我们设dp[i][j][k]为从i到j至少经过k条边的最短路,sp[i][j]意为从i到j只经过一条边的最短路,于是我们可以得到转移方程式:dp[i][k]=Min{dp[i][m][k-1]+sp[m][j]}。但是我们发现这个样复杂度并不行,于是我们考虑分块,定每一个块的大小为100,对于小于等于100的所有k我们使用上面的dp式子暴力预处理,在这之后我们将按以上方式求出的k=100时的值作为新的sp来更新所有为100的倍数的情况,注意由于题目要求是至少经过k条边而不是正好经过k条边,所以这个地方我们需要使用两点间最短距离更新一下之前求出的数组(具体实现见代码)。在有了这些预处理之后我们将给定的k分块,使得q=k/100,r=k%100,所以我们只需枚举一个v使得s到v经过r条边的最短路+v到t经过100q条边的最短路最小即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m,g[60][60],d[60][60],f[60][60],a[101][60][60],b[101][60][60],use[60][60];
inline void mul(int a[][60],int b[][60],int c[][60]){
      int i,j,k;
      memset(use,0x3f,sizeof(use));
      for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
          for(k=1;k<=n;k++)
            use[i][j]=min(use[i][j],a[i][k]+b[k][j]);
      for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
          c[i][j]=use[i][j];
}
int main(){
      int i,j,k,t;
      scanf("%d",&t);
      while(t--){
          memset(g,0x3f,sizeof(g));
          scanf("%d%d",&n,&m);
          for(i=1;i<=m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
          g[x][y]=min(g[x][y],z); 
          }
          for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
              a[0][i][j]=b[0][i][j]=(i==j?0:inf);
          for(i=1;i<=100;i++)
            mul(a[i-1],g,a[i]);
          for(i=1;i<=100;i++)
            mul(b[i-1],a[100],b[i]);
          for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
              d[i][j]=(i==j?0:g[i][j]);
          for(k=1;k<=n;k++)
            for(i=1;i<=n;i++)
              for(j=1;j<=n;j++)
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
          for(int _=0;_<=100;_++){
            memset(f,0x3f,sizeof(f));
            for(i=1;i<=n;i++)
              for(j=1;j<=n;j++)
                for(k=1;k<=n;k++)
                  f[i][j]=min(f[i][j],b[_][i][k]+d[k][j]);
            for(i=1;i<=n;i++)
              for(j=1;j<=n;j++)
                b[_][i][j]=f[i][j];
          }
          int q;
          scanf("%d",&q);
          for(i=1;i<=q;i++){
            int s,t,K;
            scanf("%d%d%d",&s,&t,&K);
            int s1=K%100,s2=K/100;
            int ans=inf;
            for(j=1;j<=n;j++)
              ans=min(ans,a[s1][s][j]+b[s2][j][t]);
            if(ans<inf)printf("%d
",ans);
              else puts("-1");
          }
      }
      return 0;
}
原文地址:https://www.cnblogs.com/yzxverygood/p/9394008.html