Loading

[USACO07NOV]牛继电器Cow Relays

https://www.luogu.org/problem/P2886

题目描述:
给出一张无向连通图,求\(S\)\(E\)经过\(k\)条边的最短路。

对于一类\(S\)\(E\)走指定数量的边数,求它的最短路或条数,都可以采用矩阵快速幂的方式解决.我们回忆一下那一个慢得惊人的\(floyd\)算法,将它的\(dp\)方程式提取出来.

\(floyd[i][j]=min(floyd[i][k]+floyd[k][j])\)

如果我们把\(floyd\)看做一个矩阵,那么矩阵乘法的标准式则为:

\(floyd[i][j]=\sum_{i=1}^{n}(floyd[i][k]×floyd[k][j])\)

我们可以寻找这个方程的本质,可以发现,\((i,j)\)的路径条数可以利用加乘原理,化为\((i,k)×(k,j)\)的和,我们可以考虑这两个\(dp\)方程有什么相似之处.可以发现,上面的那个\(dp\)方程也可以用矩阵乘法的式子替代.我们可以将上面的\(dp\)式定义为矩阵\(min\).现在我们可以考虑k的存在,走\(k\)条边的状态可以由a条边的状态与\(k-a\)条边的状态转移过来,如果我们将走k条边的状态看做一个矩阵,则\(k\)条边的状态可以正好看做\(k\)\(1\)矩阵相乘,并用矩阵快速“\(min\)"加速.矩阵\(min\)易证是满足结合律的.

原问题便转化为了求一个矩阵的\(k\)次"\(min\)".现在我们来考虑初始状态:当\(k\)=1时,就为邻接矩阵.

注意:矩阵“\(min\)"没有单位元.

#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
   int map[201][201];
};
struct lisan
{
  int num,data,newdata;
  bool operator < (const lisan &a)const
  {
    return num<a.num;
  }
};
bool cmp(lisan a,lisan b)
{
  return a.data<b.data;
}
lisan l[301];
int n,t,s,e,edges[301][3],len,map[1001];
node now,c,res;
node mul(node a,node b)
{
  for (int i=1;i<=200;++i)
    for (int j=1;j<=200;++j)
      c.map[i][j]=1e9;
  for (int i=1;i<=200;++i)
    for (int j=1;j<=200;++j)
      for (int k=1;k<=200;++k)
	c.map[i][j]=min(c.map[i][j],a.map[i][k]+b.map[k][j]);
  return c;
}
node fast_pow(node a,int b)
{
  if (b==1)
    return a;
  if (b&1)
    return mul(fast_pow(mul(a,a),b/2),a);
  else
    return fast_pow(mul(a,a),b/2);
}
int main()
{
  int useless=0;
  cin>>n>>t>>s>>e;
  for (int i=1;i<=200;++i)
    for (int j=1;j<=200;++j)
      now.map[i][j]=(1e9);
  for (int i=1;i<=t;++i)
    {
      cin>>edges[i][2]>>edges[i][0]>>edges[i][1];
      l[++len].data=edges[i][0];
      l[len].num=len;
      l[++len].data=edges[i][1];
      l[len].num=len;
    }
  sort(l+1,l+len+1,cmp);
  for (int i=1;i<=len;++i)
    {
      l[i].newdata=i-useless;
      map[l[i].data]=l[i].newdata;
      if (l[i].data==l[i+1].data)
	  useless++;
    }
  sort(l+1,l+len+1);
  for (int i=1;i<=t;++i)
      now.map[l[i*2-1].newdata][l[i*2].newdata]=now.map[l[i*2].newdata][l[i*2-1].newdata]=min(now.map[l[i*2-1].newdata][l[i*2].newdata],edges[i][2]);
  res=fast_pow(now,n);
  cout<<res.map[map[s]][map[e]]<<endl;
  return 0;
}
posted @ 2022-12-14 21:14  zhouhuanyi  阅读(30)  评论(0)    收藏  举报