Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)Total Submission(s): 633 Accepted Submission(s): 229Problem Description There are n intersections in Bytetown, connected with m one way streets. Little Q likes sport walking very much, he plans to walk for q days. On the i-th day, Little Q plans to start walking at the si-th intersection, walk through at least ki streets and finally return to the ti-th intersection.
Input The first line of the input contains an integer T(1≤T≤10), denoting the number of test cases.
|
题意:给定一个 n 个点,m 条边的有向图,q 次询问 s 到 t 经过至 少 k 条边的最短路。(2 ≤ n ≤ 50, 1 ≤ m, k ≤ 10000, 1 ≤ q ≤ 100000)
分析:比赛时没有思路,后来看啦题解补啦。由于查询次数和至少经过的边数都比较大,如果提前预处理,复杂度是10000*50*50*50,很明显会超时。每次单独计算消耗时间更多。所以可以采用分块的算法来节省时间。此题的分块是提前预处理出任意两点经过至少100*i和i条边的最小距离(i<=100),这个预处理过程可以用DP来完成。然后每次求解答案时,就可以在O(n)的时间内通过枚举中间点计算出来。(例于,要求1到n至少经过1234条边的最小距离,求枚举点p,1经过至少1200条边和p点到n至少经过34条边的距离和的最小值)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=110;
const int INF=0x3f3f3f3f;
int d[N][N][N],g[N][N][N],a[N][N],b[N][N]; //d数组存任意两点至少经过100*i条边的最小值
//g数组存任意两点至少经过i条边的最小值
int main()
{
int TA,n,m,T,u,v,x,y,z,num;
scanf("%d",&TA);
while(TA--)
{
scanf("%d%d",&n,&m);
memset(g,0x3f,sizeof(g));
memset(d,0x3f,sizeof(d));
for(int i=1; i<=n; i++)
g[0][i][i]=d[0][i][i]=0;
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&x,&y,&z);
if(g[1][x][y]>z)
g[1][x][y]=z;
}
for(int l=1; l<N; l++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
for(int k=1; k<=n; k++)
g[l][i][j]=min(g[l][i][j],g[l-1][i][k]+g[1][k][j]);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
d[1][i][j]=g[100][i][j];
for(int l=1; l<N; l++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
for(int k=1; k<=n; k++)
d[l][i][j]=min(d[l][i][j],d[l-1][i][k]+d[1][k][j]);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
a[i][j]=i==j?0:g[1][i][j];
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
for(int l=0; l<N; l++)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
{
b[i][j]=INF;
for(int k=1; k<=n; k++)
b[i][j]=min(b[i][j],d[l][i][k]+a[k][j]);
d[l][i][j]=b[i][j];
}
}
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&u,&v,&num);
x=num/100;
y=num%100;
int ans=INF;
for(int k=1; k<=n; k++)
{
ans=min(ans,g[y][u][k]+d[x][k][v]);
}
if(INF<=ans)ans=-1;
printf("%d\n",ans);
}
}
}