1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——POJ3613 Cow Replays(最短路+矩陣乘法)

藍書(演算法競賽進階指南)刷題記錄——POJ3613 Cow Replays(最短路+矩陣乘法)

題目:POJ3613.

題目大意:給出一張圖,然你求出經過N條邊後,S到T的最短路.

這道題一開始覺得挺容易的,用f[i][j]表示從起點到點i經過j的最短路,不斷更新就可以了.

但是突然發現數據巨大根本跑不過去...

然後就開始看書上的題解了...

書上居然要用矩陣乘法,好難寫的要不我放棄吧...

書上說的思路就是先離散化,然後用floyd演算法用f[i][j][k]表示從i到j的經過k條邊的最短路.

那麼我們將轉移方程列出來:

f[i][j][k]=min(f[i][t][l]+v[t][j][k-l])

然後我們會發現,每一次轉移時用來更新的邊權都是一樣的,而且點的範圍特別小,更新的次數特別多.

所以我們可以想到矩陣乘法剛好滿足這幾個性質,而且f其實就是一個矩陣!

那麼其實f就是一個“廣義矩陣乘法”,很明顯廣義矩陣乘法是滿足結合律的,所以我們就可以用快速冪優化遞推.時間複雜度O(8P^3logT),由於2P個點不一定能跑滿,所以這個演算法是能過的.

程式碼如下:

#include<iostream>
#include<cstdio>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int P=1000,T=100,INF=(1<<29)-1;
int num[P+9],ord[P+9],top;
int s,t,x[T+9],y[T+9],v[T+9];
int n,m;
struct matrix{
  int a[T*2+9][T*2+9],n,m;
  matrix(){
    n=m=0;
    for (int i=0;i<T*2+9;i++)
      for (int j=0;j<T*2+9;j++)
        a[i][j]=INF;
  }
  matrix operator * (const matrix &p)const{
    matrix tmp=matrix();
    tmp.n=n;tmp.m=p.m;
    for (int i=1;i<=n;i++)
      for (int j=1;j<=m;j++)
        for (int k=1;k<=p.m;k++)
          tmp.a[i][k]=min(tmp.a[i][k],a[i][j]+p.a[j][k]);
    return tmp;
  }
}a;
matrix power(matrix a,int k){
  matrix s=a;k--;
  for (;k;k>>=1,a=a*a)
    if (k&1) s=s*a;
  return s;
}
Abigail into(){
  scanf("%d%d%d%d",&n,&m,&s,&t);
  for (int i=1;i<=m;i++){
    scanf("%d%d%d",&v[i],&x[i],&y[i]);
    num[x[i]]++;num[y[i]]++;
  }
}
Abigail work(){
  for (int i=1;i<=1000;i++)
    if (num[i]) ord[i]=++top;
  s=ord[s];t=ord[t];
  for (int i=1;i<=m;i++)
    a.a[ord[x[i]]][ord[y[i]]]=a.a[ord[y[i]]][ord[x[i]]]=min(a.a[ord[x[i]]][ord[y[i]]],v[i]);
  a.n=a.m=top;
  a=power(a,n);
}
Abigail outo(){
  printf("%d\n",a.a[s][t]);
}
int main(){
  into();
  work();
  outo();
  return 0;
}