蓝书(算法竞赛进阶指南)刷题记录——POJ3613 Cow Replays(最短路+矩阵乘法)

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/83097897

题目: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;
} 

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/83097897