参考于:08年论文:俞华程《矩阵乘法在信息学中的应用》


图邻接矩阵上的乘法:

             图的邻接矩阵可以唯一地表示一张图,并且有很多神奇的性质。接下来我们 将研究邻接矩阵上的矩阵乘法。

        首先,我们来看一下最简单的情况,一张N个点的无向无权图。如果点a和 点b连边,那么邻接矩阵G[a,b] = G[b,a] = 1,否则都等于0。考虑邻接矩阵自 乘,即G2:

                  G2 [a,b] = N ∑ i=1 G[a,i]G[i,b]                                    (9) 


         上式中,G[a,i],G[i,b]的值或者等于0或者等于1。易知当且仅当G[a,i], G[i,b]都是1的时候值为1,即如果从点a到点i、点i到b都有边,那么值为1。 而G2 [a,b]的值就等于值为1的项数,也就是从点a到点b经过1个中间点的路径条 数。我们继续考虑G3:

                  G3 [a,b] =N ∑ i=1N ∑ j=1G[a,i]G[i,j]G[j,b]                 (10)


        和G2很相似,G[a,i]G[i,j]G[j,b] = 1当且仅当G[a,i] = G[i,j] = G[j,b] = 1,也就是说a−i−j−b组成一条路经。
那么G3 [a,b]的值就是从点a到点b经过2个 中间点的路径条数。从另一个方面来说,G3 = G2G,从点a到点b经过2个中间 点的路径中,第二个中间点是i的路径条数恰好为G2 [a,i]G[i,b],从这里也可 以说明上一个结论。那么更一般的,Gk [a,b]是否就等就a到b经过k −1个中间 点也就是长度为k的路径条数呢?答案是肯定的。和前面的推导很相似,可以 使用归纳法证明,Gk = Gk−1G,那么

                  Gk [a,b] =N ∑ i=1Gk−1 [a,i]G[i,b]                              (11)


        其中Gk−1 [a,i]就是从a到i长度为k−1的路径条数,G[i,b]则表示了i到b是否 有路径,两者相乘恰好就是从a到b长度为k的路径上倒数第二个点为i的路径条 数。当k = 0时,G0为单位矩阵,G0 [a,a] = 1也符合a到a有一条长度为0的路 径。前面的证明当中并没有用到任何无向图的信息,因此这个结论也适用于有 向图。

          那么对于有重边的图,我们只需要把G[a,b]改为表示点a,b之间重边的条 数即可。证明和前面的几乎一样,此处就不在赘述。而对于有向图的自环, 不需要特殊考虑。对于无向图的自环,我们通常是把G[a,a]加上2,这样在计 算Gk的时候这条边就被算成2条不同的边,如果只加上1,又会不满足矩阵里所 有元素和等于边数两倍的性质。需要怎样应具体情况具体分析。



poj3613:

Cow Relays
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 7357   Accepted: 2887

Description

For their physical fitness program, N (2 ≤ N ≤ 1,000,000) cows have decided to run a relay race using the T (2 ≤ T ≤ 100) cow trails throughout the pasture.

Each trail connects two different intersections (1 ≤ I1i ≤ 1,000; 1 ≤ I2i ≤ 1,000), each of which is the termination for at least two trails. The cows know the lengthi of each trail (1 ≤ lengthi  ≤ 1,000), the two intersections the trail connects, and they know that no two intersections are directly connected by two different trails. The trails form a structure known mathematically as a graph.

To run the relay, the N cows position themselves at various intersections (some intersections might have more than one cow). They must position themselves properly so that they can hand off the baton cow-by-cow and end up at the proper finishing place.

Write a program to help position the cows. Find the shortest path that connects the starting intersection (S) and the ending intersection (E) and traverses exactly N cow trails.

Input

* Line 1: Four space-separated integers: NTS, and E
* Lines 2..T+1: Line i+1 describes trail i with three space-separated integers: lengthi , I1i , and I2i

Output

* Line 1: A single integer that is the shortest distance from intersection S to intersection E that traverses exactly N cow trails.

Sample Input

2 6 6 4
11 4 6
4 4 8
8 4 9
6 6 8
2 6 9
3 8 9

Sample Output

10

Source


题目大意:

               给定一张M条边的无向带权图,求从起点S到终点E恰好经过K条边的最短 路径。2≤ M ≤100,2≤ K ≤1000000。保证每个连边的点度至少为2。 


题目思路:

             有了前面矩阵乘法的概念,所以我们不妨设ans[i][j]^k表示为i->j经过k条边的最短路,而应为k很大,所以我们要用到矩阵快速幂,而对于题目给的点我们可以离散化下这样最大就只有100个点了,我们初始时用map[i][j]来保存地图,这里求矩阵的乘我们可以借用floyd,我们知道floyd的原理是更新以k为中间点的最短路,而我们的邻接矩阵的乘G[i][j]*G[i][j] 也就是找中间的k使得i->k和k->j

都有路而对于最短路矩阵乘我们可以在遍历k时取个最短的,也就是c[i][j]=min(a[i][k]+b[k][j],1<=k<=n)a,b为两个矩阵如果a是经过x条边b是经过y条边那么c就是经过x+y条边的最短路,这个很好想到,所以最后面我们只需将map跑k遍floyd就是答案,但是k太大,所以要二分矩阵也就是矩阵快速幂!


AC代码:


#include<cstring>
#include<cstdio>

#define min(x,y) (x<y?x:y)

const int inf = 0x3f3f3f3f;

class MatrixFloyd{
public:

    int n,m,s,t,k;
    int ans[205][205],tmp[205][205],mp[205][205],dis[205][205],v[1005];

    void init(){
        n=0;
        memset(v,-1,sizeof(v));
        for(int i=0;i<200;i++){
            for(int j=0;j<200;j++)
                ans[i][j]=tmp[i][j]=mp[i][j]=dis[i][j]=inf;
            ans[i][i]=0;
        }
    }

    void in(){
        init();
        scanf("%d%d%d%d",&k,&m,&s,&t);
        while(m--){
            int w,x,y;scanf("%d%d%d",&w,&x,&y);
            if(v[x]==-1)v[x]=n++;                //将点离散化
            if(v[y]==-1)v[y]=n++;
            mp[v[x]][v[y]]=mp[v[y]][v[x]]=min(mp[v[x]][v[y]],w);
        }
        sove();
        printf("%d\n",ans[v[s]][v[t]]);
    }

    void floyd(int c[][205],int a[][205],int b[][205]){
        for(int k=0;k<n;k++)
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)
                    c[i][j]=min(c[i][j],a[i][k]+b[k][j]);
    }

    void sove(){
        while(k){
            if(k&1){               //二进制位为1
                floyd(dis,ans,mp);              //用dis保存ans和mp相乘的矩阵,即c=a+b
                memcpy(ans,dis,sizeof(ans));    //赋值给ans
                memset(dis,0x3f,sizeof(dis));   //在初始化dis
            }
            floyd(tmp,mp,mp);
            memcpy(mp,tmp,sizeof(mp));            //mp表示的是mp^2^n  n为二进制长度
            memset(tmp,0x3f,sizeof(tmp));
            k>>=1;
        }
    }


}mf;

int main()
{
    mf.in();
    return 0;
}