HDU 6331 2018HDU多校赛 第三场 Walking Plan(最短路+矩阵优化+分块)

Problem M. Walking Plan

Time 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.
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

 

Source

2018 Multi-University Training Contest 3

大致题意:给你一个含有不超过50个点的图,然后有q个询问,每次给出三个数字s、t和k,问从s出发走到t,至少需要经过k条边的最短路径是多少。

非常厉害的一道题,曾经我以为自己对矩阵的所有用途已经很了解了,但是这次再一次让我发现自己真的还是太无知……

首先,我们考虑最初状态的邻接矩阵的含义,f[i][j]表示从i走到j最近的一条边的长度,那么我们也可以看成是从i到j经过一条边的最短路。然后,如果考虑这个矩阵自己与自己进行矩阵“乘法”,这里打个引号因为这个乘法的含义不同于普通的矩阵乘法,具体来说,我们定义乘法为:ans[i][j]=max(f[i][k]+f[k][j]),即每次都是取一个最小值,那么很显然,这个ans[i][j]就可以表示从i走到j恰好经过两条边的最短路。如果进行两次乘法,那么就是从i走到j恰好经过三条边的最短路,以此类推,f的n次方就对应为i到j走恰好n条路的最短路。由此,矩阵快速幂的性质也可以利用了。

但是,虽说可以用快速幂在O(N^3*\log_2K)的时间复杂度内解决一个询问,但是对于最多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),总的时间复杂度就是O(2*N^3*\sqrt N+qN)

具体实现的话,由于本题的询问是问至少经过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;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/81293967
今日推荐