poj3613Cow Relays (经过n条边的最短路)

//poj 3613
//求经过n条边的最短路
//此题让我更深入的了解了最短路的floyed算法
//首先对于一个图的可达矩阵A而言(可达为1,不可达为0),A表示<i,j>是否直接可达。
//A*A表示<i,j>是否中间经过一个点k可达(因为是累加所有情况,只要有一个k可达,数字大于1,即为可达)。
//那么我们求的这个数字除了表示是否可达,有没有别的含义呢?
//在矩阵乘法过程中,我们做的累加实际上就是加法原理的累加,我们累加的结果是<i,j>的路径条数。
//对于<i,k>和<k,j>而言,假设<i,k>的路径条数为Aik,由于k-j是确定的,那么如果经过k到j就有Aik种可能性。那么是否可以经过k呢?取决于k-j是否有路,也就是Akj是否大于0.
//所以做了几次矩阵乘法,就相当于插入了几个点。

//回到本问题
//如果事先定义最短路只有经过边才成立的话
//在邻接矩阵中dis[i][i]=INF;
//floyed的过程实际上是类似于矩阵乘法的,只不过加了个限制条件。(dis[i][j]=dis[i][k]+dis[k][j])
//我们既然可以用矩阵乘法模拟插入点的过程计算经过x条边的了路径条数,
//那么也可以用floyed乘法来模拟入点的过程计算经过x条边的了最短路径长度,
//一次floyed后所求的矩阵就是在i,j中插入一个点的k的最短路。
//用当前矩阵再进行floyed即,再向<i,j>中插入点k',此时<i,j>中有两个点k,k'.在i,j中插入两个点的最短路。
//以前学的floyed更新最短路,虽然思想上是插入一个点,但是因为每次都只用dis一个数组,实际上更新过的点都会再次更新。
//我们每次都用新的数组,保留以前的数组,只用以前的数组进行更新,那么就可以保证,每次只用一点来更新<i,j>
//需要注意的是,我们进行的第一次floyed实际上是与自身进行的。初始化ans[i][i]=0;保证每次只并入端点。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int N=300;
const int INF=0x3fffffff;
#define clr(a) memset(a,0,sizeof(a))
int map[N][N],temp[N][N],ans[N][N];
int used[10*N];
int p[10*N];
//c为待更新数组,初始化为INF,b为<i,j>中插了x个点的距离,a为<i,j>中插了y个点的距离
//正是由于c是一个完全新的数组,所以我们更新的时候可以看成一个新图
//每次插入一个点进行更新。b代表<i,k>间有x个点,且不管这x个点是怎么走的,可以重复走。
//a代表从<k,j>有y个点,是一个不断向外合并的过程。
void floyed(int a[][N],int b[][N],int c[][N],int cnt){
    for(int k=1;k<=cnt;k++){
        for(int i=1;i<=cnt;i++){
            for(int j=1;j<=cnt;j++){
                if(a[i][k]+b[k][j]<c[i][j])
                    c[i][j]=a[i][k]+b[k][j];
            }
        }
    }
}

void copy(int n,int a[][N],int b[][N]){
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
            a[i][j]=b[i][j],b[i][j]=INF;
}
int solve(int s,int t,int n,int cnt){
 
    while(n){
        if(n&1){
            floyed(ans,map,temp,cnt);
            copy(cnt,ans,temp);
        }
        floyed(map,map,temp,cnt);
        copy(cnt,map,temp);
        n>>=1;
    }
    return ans[s][t];
}
 int main(){
    int n,t,S,E;
     clr(used);
     scanf("%d%d%d%d",&n,&t,&S,&E);
     int u,v,w;
     int cnt=0;
     for(int i=0;i<=250;i++){
         for(int j=0;j<=250;j++){
             ans[i][j]=temp[i][j]=map[i][j]=INF;
         }
         ans[i][i]=0;
     }
     for(int i=0;i<t;i++){
         scanf("%d%d%d",&w,&u,&v);
         if(!used[u]){
             used[u]=1;
             p[u]=++cnt;
         }
         if(!used[v]){
             used[v]=1;
             p[v]=++cnt;
         }
         map[p[u]][p[v]]=map[p[v]][p[u]]=w;
     }
     printf("%d\n",solve(p[S],p[E],n,cnt));
     return 0;
}
/*
 5 6 1 5
 1 2 1
 1 5 3
 2 3 2
 2 5 2
 3 4 1
 4 5 2
 */

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值