2014 Super Training #10 G Nostop --矩阵快速幂

原题: FZU 2173 http://acm.fzu.edu.cn/problem.php?pid=2173

一开始看到这个题毫无头绪,根本没想到是矩阵快速幂,其实看见k那么大,就应该想到用快速幂什么的,况且n<=50,可以用矩阵来表示图。

1.为什么能用矩阵快速幂呢?

原理:

原始矩阵m[][]中,m[u][v]代表u到v的花费,求矩阵的k次幂后,此时m[u][v]代表,从u走向b经过v步的最少花费
注意此时矩阵的相乘应该写成:
m[a][b]=min(m[a][1]+m[1][b],...m[a][n]+m[n][b])  ,即取最小值而非相加。

2.为什么呢?

m的1次方,无疑是正确的。

m的2次方
此时(m[a][b])^2=min(m[a][1]+m[1][b],..m[a][n]+m[n][b]),就是枚举a经过1到n点再到b的最少花费,就是a经过两步到达b的最少花费


归纳法:

如果(m[a][b])^i代表了a走i步到达b的最少花费,则m^(i+1)=min((m[a][1])^i+m[1][b],...(m[a][n])^i+m[n][b])

所以可以这样做。

(借鉴nothing的博客)

代码:

#include <iostream>

#include <cstdio>

#include <cstring>

#include <cmath>

#include <cstdlib>

#include <algorithm>

#define Mod 1000000007

#define lll __int64

using namespace std;

#define N 6007



struct Matrix

{

    lll m[55][55];

};

int n,h,k;



Matrix Mul(Matrix a,Matrix b)

{

    Matrix c;

    memset(c.m,-1,sizeof(c.m));

    for(int i=0;i<n;i++)

        for(int j=0;j<n;j++)

        {

            for(int k=0;k<n;k++)

            {

                if(a.m[i][k]!=-1&&b.m[k][j]!=-1)

                {

                    if(c.m[i][j] == -1)

                        c.m[i][j] = a.m[i][k]+b.m[k][j];

                    else

                        c.m[i][j] = min(c.m[i][j],a.m[i][k]+b.m[k][j]);

                }

            }

        }

    return c;

}



Matrix fastm(Matrix a,int n)

{

    if(n == 1)

        return a;

    Matrix res = fastm(a,n/2);

    res = Mul(res,res);

    if(n&1)

        res = Mul(res,a);

    return res;

}



Matrix MPow(Matrix a,int n)  //第二种写法

{

    Matrix res = a;

    n--;

    while(n)

    {

        if(n&1)

            res = Mul(res,a);

        n>>=1;

        a = Mul(a,a);

    }

    return res;

}



int main()

{

    int t,i,j,k;

    int u,v;

    lll w;

    Matrix A,ans;

    scanf("%d",&t);

    while(t--)

    {

        scanf("%d%d%d",&n,&h,&k);

        memset(A.m,-1,sizeof(A.m));

        for(i=0;i<h;i++)

        {

            scanf("%d%d%I64d",&u,&v,&w);

            u--,v--;

            if(A.m[u][v] == -1)

                A.m[u][v] = w;

            else

                A.m[u][v] = min(A.m[u][v],w);

        }

        ans = MPow(A,k);

        printf("%I64d\n",ans.m[0][n-1]);

    }

    return 0;

}
View Code

 

你可能感兴趣的:(super)