Problem M. Walking PlanTime Limit: 5000/2500 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)Total Submission(s): 185 Accepted Submission(s): 46 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. Input The first line of the input contains an integer T(1≤T≤10) , denoting the number of test cases. 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 Source |
大致题意:给你一个含有不超过50个点的图,然后有q个询问,每次给出三个数字s、t和k,问从s出发走到t,至少需要经过k条边的最短路径是多少。
非常厉害的一道题,曾经我以为自己对矩阵的所有用途已经很了解了,但是这次再一次让我发现自己真的还是太无知……
首先,我们考虑最初状态的邻接矩阵的含义,f[i][j]表示从i走到j最近的一条边的长度,那么我们也可以看成是从i到j经过一条边的最短路。然后,如果考虑这个矩阵自己与自己进行矩阵“乘法”,这里打个引号因为这个乘法的含义不同于普通的矩阵乘法,具体来说,我们定义乘法为:,即每次都是取一个最小值,那么很显然,这个ans[i][j]就可以表示从i走到j恰好经过两条边的最短路。如果进行两次乘法,那么就是从i走到j恰好经过三条边的最短路,以此类推,f的n次方就对应为i到j走恰好n条路的最短路。由此,矩阵快速幂的性质也可以利用了。
但是,虽说可以用快速幂在的时间复杂度内解决一个询问,但是对于最多10W个询问来说还是回TLE。于是我们考虑分块预处理。由于K最多为1W,那么我们可以把K分为两部分相加,即K=100*A+B,其中A=K/100,B=K%100。然后我们实现预处理f的1……100次方,和f的100,200,300,...,10000次方,总共进行200次矩阵乘法。然后,每次询问,我可以按照两个部分来计算,每次枚举一个中间点x,用100*A步来从s走到x,再用B步来从x走到t,在所有的x中选取一个最优的即可。如此一来,单个询问的复杂度就是O(N),总的时间复杂度就是。
具体实现的话,由于本题的询问是问至少经过K步,所以,我们还要考虑实际走的步数大于K步的时候有没有可能有更优的结果。因此,我们再从大到小进行一个比较,步数少的对应的可以继承步数多的的结果。另外,还要尝试多走一些步数,看是否会更优。然后对于A==0和B==0的时候,还要做一些特殊的判断。具体见代码:
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define mod 1000000007
#define N 51
#define LL long long
using namespace std;
int n,m,q;
struct Matrix
{
LL a[N][N];
Matrix operator *(Matrix x) const
{
Matrix ans;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
ans.a[i][j]=1e18;
for(int k=1;k<=n;k++)
ans.a[i][j]=min(ans.a[i][j],a[i][k]+x.a[k][j]);
}
return ans;
}
} g,g100[110],gg[110];
int main()
{
int T;
IO; cin>>T;
while(T--)
{
memset(g.a,INF,sizeof(g.a));
cin>>n>>m;
while(m--)
{
LL x,y,w;
cin>>x>>y>>w;
g.a[x][y]=min(g.a[x][y],w);
}
cin>>q; gg[1]=g;
for(int i=2;i<=101;i++) gg[i]=gg[i-1]*g; g100[1]=gg[100];
for(int i=2;i<=101;i++) g100[i]=g100[i-1]*gg[100];
for(int i=99;i>=1;i--)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
{
gg[i].a[j][k]=min(gg[i].a[j][k],gg[i+1].a[j][k]);
g100[i].a[j][k]=min(g100[i].a[j][k],g100[i+1].a[j][k]);
}
gg[0]=gg[1];
memset(g100[0].a,INF,sizeof(g100[0].a));
while(q--)
{
int s,t,k;
cin>>s>>t>>k;
LL a=k/100,b=k%100,ans=1e18;
for(int i=1;i<=n;i++)
{
ans=min(ans,g100[a].a[s][i]+gg[b].a[i][t]);
ans=min(ans,g100[a+1].a[s][i]+gg[0].a[i][t]);
}
ans=min(ans,g100[a+1].a[s][t]);
if (a==0) ans=min(ans,gg[k].a[s][t]);
if (b==0) ans=min(ans,g100[a].a[s][t]);
if (ans==1e18) cout<<-1<<endl; else cout<<ans<<endl;
}
}
return 0;
}