Problem M. Walking Plan
Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 446 Accepted Submission(s): 149
Problem 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.
Little Q's smart phone will record his walking route. Compared to stay healthy, Little Q cares the statistics more. So he wants to minimize the total walking length of each day. Please write a program to help him find the best route.
Input
The first line of the input contains an integer T(1≤T≤10), denoting the number of test cases.
In each test case, there are 2 integers n,m(2≤n≤50,1≤m≤10000) in the first line, denoting the number of intersections and one way streets.
In the next m lines, each line contains 3 integers ui,vi,wi(1≤ui,vi≤n,ui≠vi,1≤wi≤10000), denoting a one way street from the intersection ui to vi, and the length of it is wi.
Then in the next line, there is an integer q(1≤q≤100000), denoting the number of days.
In the next q lines, each line contains 3 integers si,ti,ki(1≤si,ti≤n,1≤ki≤10000), describing the walking plan.
Output
For each walking plan, print a single line containing an integer, denoting the minimum total walking length. If there is no solution, please print -1.
Sample Input
2
3 3
1 2 1
2 3 10
3 1 100
3
1 1 1
1 2 1
1 3 1
2 1
1 2 1
1
2 1 1
Sample Output
111
1
11
-1
题目大意:给定一个n个点,m条边的有向图,边上有权值,总共进行q次询问,每次询问给出s,t,k,询问从s到t至少经过k条边的最短路是多少。
题目思路:如果对于每次查询考虑直接求解的话,每次询问的复杂度都为O(n^3logk),这个复杂度是无法很好的解决这个问题的。所以我们考虑进行分块求解,首先我们可以通过预处理处理出如下几个值
dis1[s][i][j]:表示从 i 到 j 至少走过s条边时的最短路
dis2[s][i][j]:表示从 i 到 j 刚好走过100*s条边时的最短路
由于k<=10000,所以每次询问我们可以将 k 分解为 k = A + B*100 ,A=k%100,B=k/100,那么最终的结果就为
ans = min{dis1[A][s][u]+dis2[B][u][t])
这样每次查询时我们只需要枚举中间点 u ,就可以求出答案了。,每次询问的复杂度降为了O(n)。
具体实现看代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+r,rt<<1|1
#define lowbit(x) x&-x
#define pb push_back
#define MP make_pair
#define clr(a) memset(a,0,sizeof(a))
#define clm(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define fuck(x) cout<<"["<<x<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
const int MX=50+7;
const int maxn=105;
const int inf=0x3f3f3f3f;
int n,m,q,_;
int d[MX][MX];
int dis1[maxn][MX][MX],dis2[maxn][MX][MX],f[MX][MX];
void get_min(int &x,int y){
x=min(x,y);
}
void pre_solve(){
for(int s=1;s<maxn;s++){
//dis1[s][i][j]:刚好走过s条边时,从i走到j的最短路
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
get_min(dis1[s][i][j],dis1[s-1][i][k]+d[k][j]);
}
}
}
}
for(int s=1;s<maxn;s++){
//dis2[s][i][j]:刚好走过100*s条边时,从i走到j的最短路
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
get_min(dis2[s][i][j],dis2[s-1][i][k]+dis1[100][k][j]);
}
}
}
}
for(int i=1;i<=n;i++) d[i][i]=0;
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
get_min(d[i][j],d[i][k]+d[k][j]);
}
}
for(int s=0;s<maxn;s++){
//更新dis1,dis1[s][i][j]表示至少走过s条边时从i到j的最短路;
clm(f);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
get_min(f[i][j],dis1[s][i][k]+d[k][j]);
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dis1[s][i][j]=f[i][j];
}
}
}
}
int main(){
//FIN;
for(scanf("%d",&_);_;_--){
scanf("%d%d",&n,&m);
clm(d);clm(dis1);clm(dis2);
for(int i=1;i<=n;i++) dis1[0][i][i]=dis2[0][i][i]=0;
for(int i=1;i<=m;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
d[u][v]=min(d[u][v],w);
}
pre_solve();
scanf("%d",&q);
while(q--){
int s,t,k;
scanf("%d%d%d",&s,&t,&k);
int ans=inf;
int A=k%100,B=k/100;
for(int u=1;u<=n;u++)
get_min(ans,dis1[A][s][u]+dis2[B][u][t]);
if(ans>=inf) ans=-1;
printf("%d\n",ans);
}
}
return 0;
}