POJ 3613Cow Relays( floyd 倍增法)

题意:从s点出发到达e点且n条边的最短路是多少(可以走重复的路径)

图中点<=200,n<=1000,000


思路:folyd可以实现向路径中添边,但是这题与普通的求最短路问题不一样,比如从S到E经过X条边后就已经达到了最短路,这个时候仍然要强制用folyd再添边,尽管添边后就不是最短路了,但是要注意到添加的这边要使最短路损失最小,抓住这点用folyd可以实现强制添边的操作,所以可以从n=1的状态向n的状态转移


所以可以对原来的map进行n-1次folyd,但是n的范围太大,最坏的情况要进行1000000*200^3次运算肯定会T


这时,注意到可以用倍增法加速状态的转移,任何n都可以分解成2^i的和,每个2^i的状态都可以由2^(i-1)的状态直接求得。

也就是当前的map中每个点之间的距离表示经过了2^(i-1)条边,然后对当前的map folyd一次,就可以算出map中每个点之间经过了2^(i-1)+2^(i-1)=2^i 条边的最短路

所以用倍增法加速了状态转移

//628K	94MS
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define M 205
int n,m,st,ed;
int Hash[1005],cnt;
int mapp[M][M],tmp[M][M],n_ans[M][M];

void folyd(int a[M][M],int b[M][M],int c[M][M]){
    for(int k=1;k<=cnt;k++)
    for(int j=1;j<=cnt;j++)
    for(int i=1;i<=cnt;i++)
        if(a[j][i]>b[j][k]+c[k][i])
            a[j][i]=b[j][k]+c[k][i];

}
void map_copy(int a[M][M],int b[M][M]){
    for(int i=1;i<=cnt;i++)
    for(int j=1;j<=cnt;j++){
        a[i][j]=b[i][j];
        b[i][j]=inf;
    }
}
int main(){

    memset(mapp,0x3f,sizeof(mapp));
    memset(tmp,0x3f,sizeof(tmp));
    memset(n_ans,0x3f,sizeof(n_ans));
    for(int i=1;i<=200;i++){
        n_ans[i][i]=0;
        //注意不要让 mapp[i][i]=tmp[i][i]=0,因为要使tmp[i][i]任何时候无穷大因为自身到自身也要通过其他多个牛
        //mapp[i][j]不是inf的值表示必须存在边,不能假定自身到自身是一条权为0的边
        //n_ans[i][i]初始化自身到自身为0是动态规划的边界条件
    }
    scanf("%d%d%d%d",&n,&m,&st,&ed);
    while(m--){
        int val,u,v;
        scanf("%d%d%d",&val,&u,&v);
        if(!Hash[u]){ //对顶点离散化
            Hash[u]=++cnt;
        }
        if(!Hash[v]){
            Hash[v]=++cnt;
        }
        mapp[Hash[u] ][Hash[v] ]=mapp[Hash[v] ][Hash[u] ]=val; 
    }
    while(n){
        if(n&1){
            folyd(tmp,n_ans,mapp);
            map_copy(n_ans,tmp);
        }
        folyd(tmp,mapp,mapp);
        map_copy(mapp,tmp);
        n>>=1; //加速状态转移
    }
    printf("%d\n",n_ans[Hash[st] ][Hash[ed] ]);
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值