Problem M. Walking Plan
Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 883 Accepted Submission(s): 322
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
题目:
给你一个有向图,然后有q次询问,每一次询问s,t,k,表示s->t的经过至少k条边的最短路长度
解析:
官方题解:
设G[i][j]表示i一步到j的最短路,f[t][i][j]表示i经过恰好t条边到达j的最短路,则。f具有更强的性质,即
。将合并的过程看作乘法,则
。
注意到,设
,
,将每个询问s,t,k拆成两步:
- 从s出发经过恰好100*A条边到达某个点u。
- 从u出发经过至少B条边到达t。
设,
,那么a[i]即为经过恰好100i条边的最短路,对于b[i]需要再进行一次Floyd得到至少i条边的最短路。
枚举中间点u,则。
时间复杂度。
首先分块,这里把对k进行分块。但是不是简单的将询问分成k'<和k'>
的情况,因为其实这里好像也没有什么空间的问题,
,数组完全够用。所以我们把他进行纵向的分块将每一次查询分成两块来处理,一块是
和
即上面题解说的两步。
然后就是求A,B的数组。这里其实只需要这一条性质就可以了。其实这个好像更像是动态规划。G[k][j]其实就是初始的图,但是注意,要把G[i][i]=INF,因为i走一步是走不到i的。然后我们就可以根据这个式子对于第一维[t]从下往上递推就可以了,对于里面的那两维[i][j]我们可以用Floyd()来做,所以总共和在一起就是4个循环,
的复杂度。然后求出B,与上面类似,只不过这里的G[k][j]变成了A[100][k][j],道理同上。然后我们还需要让B变成至少i条的最短路,这个其实就是很简单的dp,B[k][j][i]=min(B[k+1][j][i]) j∈[99,0]。最后询问的按照题解说的那样,遍历n项求min求行了。
我看很多大佬都是把A,B当矩阵来求的,代码写的很简单。上面题解也有说,把它当成矩阵,我们只需要重载矩阵的*,重载的代码其实就是Floyd的代码。
我这里代码上是A=Gi,B=G100i,
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <queue>
#include <vector>
#include <algorithm>
#define lch root<<1
#define rch (root<<1)|1
#define mk make_pair
#define pb push_back
using namespace std;
typedef long long ll;
const int MAXN = 1e2+2;
#define Max 60
#define INF 0x3f3f3f3f
typedef struct graph *Graph;
typedef pair<int,int> PII;
typedef struct node
{
int s,x;
int k;
int dis;
}node;
typedef struct graph
{
int e , n;
int data[Max][Max];
}graph;
int A[MAXN][Max][Max];
//int Path[Max][Max];
int B[MAXN][Max][Max];
void Floyd(Graph g)
{
int i , j , k , w;
for(w = 0 ; w < MAXN ; w++)
for(i = 1 ; i <= g->n ; i++)
{
for(j = 1 ; j <= g->n ; j++)
{
if(i==j&&w==0) A[w][i][j]=0;
else A[w][i][j]=INF;
}
}
for(w = 1 ; w < MAXN ; w++)
{
for(i = 1 ; i <= g->n ; i++)
{
for(j = 1 ; j <= g->n ; j++)
{
for(k = 1 ; k <= g->n ; k++)
{
if(A[w][j][k] > A[w-1][j][i] + g->data[i][k])
{
A[w][j][k] = A[w-1][j][i] + g->data[i][k];
}
}
}
}
}
}
void Floyd100(Graph g)
{
int i , j , k , w;
for(w = 0 ; w < MAXN ; w++)
for(i = 1 ; i <= g->n ; i++)
{
for(j = 1 ; j <= g->n ; j++)
{
if(i==j&&w==0) B[w][i][j]=0;
else B[w][i][j]=INF;
}
}
for(w = 1; w < MAXN ; w++)
{
for(i = 1 ; i <= g->n ; i++)
{
for(j = 1 ; j <= g->n ; j++)
{
for(k = 1 ; k <= g->n ; k++)
{
if(B[w][j][k]>B[w-1][j][i]+A[100][i][k])
{
B[w][j][k]=B[w-1][j][i]+A[100][i][k];
}
}
}
}
}
}
void Floydmin(Graph g)
{
int i,j,w,k;
for(w = MAXN-2 ; w >= 0 ; w--)
for(i = 1 ; i <= g->n ; i++)
{
for(j = 1 ; j <= g->n ; j++)
{
A[w][i][j]=min(A[w][i][j],A[w+1][i][j]);
}
}
}
graph g;
int ans=INF;
int main()
{
int t;
int n,m;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
g.n=n;
g.e=m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
/*if(i==j) g.data[i][j]=0;
else */g.data[i][j]=INF; //data[i][j]表示i->j走一步的最短路
}
for(int i=0;i<m;i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
g.data[x][y]=min(w,g.data[x][y]);
}
Floyd(&g);
Floyd100(&g);
Floydmin(&g);
/*printf("%d\n",B[1][2]);
showPath(1,2);*/
int q;
scanf("%d",&q);
int s,e,k;
for(int i=0;i<q;i++)
{
scanf("%d%d%d",&s,&e,&k);
ans=INF;
for(int i=1;i<=g.n;i++)
{
ans=min(ans,B[k/100][s][i]+A[k%100][i][e]);
}
if(ans==INF) printf("-1\n");
else printf("%d\n",ans);
}
}
return 0;
}