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

本文深入探讨POJ3613题目,解析如何通过离散化和Floyd算法解决大规模图中特定边数的最短路径问题。采用广义矩阵乘法与快速幂优化递推过程,实现高效算法设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:POJ3613.
题目大意:给出一张 t t t个点的图,然你求出经过 n n n条边后, S S S T T T的最短路.
1 ≤ n ≤ 1 0 6 , 1 ≤ t ≤ 100 1\leq n\leq 10^6,1\leq t\leq 100 1n106,1t100.

这道题一开始觉得挺容易的,用 f [ i ] [ j ] f[i][j] f[i][j]表示从起点到点 i i i经过 j j j的最短路,不断更新就可以了.

但是突然发现数据巨大根本跑不过去,然后就开始看书上的题解了…

书上说的思路就是先离散化,然后用floyd算法用 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示从 i i i j j j的经过 k k k条边的最短路.

那么我们将转移方程列出来:
f [ i ] [ j ] [ k ] = m i n { f [ i ] [ t ] [ l ] + v [ t ] [ j ] [ k − l ] } f[i][j][k]=min\{f[i][t][l]+v[t][j][k-l]\} f[i][j][k]=min{f[i][t][l]+v[t][j][kl]}

然后我们会发现,每一次转移时用来更新的边权都是一样的,而且点的范围特别小,更新的次数特别多.

所以我们可以想到矩阵乘法刚好满足这几个性质,而且 f f f其实就是一个矩阵!

那么其实 f f f就是一个“广义矩阵乘法”,很明显广义矩阵乘法是满足结合律的,所以我们就可以用快速幂优化递推.时间复杂度 O ( P 3 l o g T ) O(P^3logT) O(P3logT),由于 2 P 2P 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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值