杭电多校第三场 1013 HDU-6311 Walking Plan(Floyd最短路)

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;
}

猜你喜欢

转载自blog.csdn.net/Lee_w_j__/article/details/81302966