[USACO07NOV]Cow Relays - 最短路 - Floyd算法

[题目描述]
给定一张由T条边构成的无向图,点的编号为1~1000之间的整数。
求从起点S到终点E恰好经过N条边(可以重复经过)的最短路。
注意: 数据保证一定有解。
[题目链接] https://www.luogu.com.cn/problem/P2886
[解析]
floyd的变形应用。
边数T<=200,点数N<=10^6,显然应该想到可以对点编号做一个离散化,映射到1~2T范围内。点数骤降,是不是想到了Floyd?Floyd每次选择中间点k插入i->j的最短路相当于最短路增加一条边。
用A[i][j]表示i到j只经过一条路径的最短路,B[i][j]表示从i到j只经过两条路径的最短路,有
B [ i ] [ j ] = m i n 1 < = k < = c n t m i n ( B [ i ] [ j ] , A [ i ] [ k ] + A [ k ] [ j ] ) B[i][j] = min_{1<=k<=cnt} min(B[i][j],A[i][k]+A[k][j]) B[i][j]=min1<=k<=cntmin(B[i][j],A[i][k]+A[k][j])

	int tmp[maxn][maxn];
    memset(tmp,0x3f,sizeof(tmp));
    for(int k=1;k<=cnt;k++)
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                tmp[i][j] = min(tmp[i][j],x[i][k]+y[k][j]);
    

A x [ i ] [ j ] A^x[i][j] Ax[i][j]表示从i到j经过x条路径的最短路, A y [ i ] [ j ] A^y[i][j] Ay[i][j]表示i到j经过y条路径的最短路
A x + y [ i ] [ j ] = m i n 1 < = k < = c n t m i n ( A x + y [ i ] [ j ] , A x [ i ] [ k ] + A y [ k ] [ j ] ) A^{x+y}[i][j] = min_{1<=k<=cnt} min(A^{x+y}[i][j],A^x[i][k]+A^y[k][j]) Ax+y[i][j]=min1<=k<=cntmin(Ax+y[i][j],Ax[i][k]+Ay[k][j])
N < = 1 0 6 N<=10^6 N<=106,用快速幂加速运算。
代码如下。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 205;
unordered_map<int,int> mp;
int cnt = 0;
int N,T,S,E;
int A[maxn][maxn],B[maxn][maxn];
void read()
{   memset(A,0x3f,sizeof(A));
    int u,v,w;
    scanf("%d%d%d%d",&N,&T,&S,&E);
    for(int i=1;i<=T;i++)
    {
        scanf("%d%d%d",&w,&u,&v);
        if(!mp[u])  mp[u] = ++cnt;
        if(!mp[v])  mp[v] = ++cnt;
        A[mp[u]][mp[v]] = min(A[mp[u]][mp[v]],w);
        A[mp[v]][mp[u]] = A[mp[u]][mp[v]];
    }
}
void floyd(int x[maxn][maxn],int y[maxn][maxn])
{
    int tmp[maxn][maxn];
    memset(tmp,0x3f,sizeof(tmp));
    for(int k=1;k<=cnt;k++)
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                tmp[i][j] = min(tmp[i][j],x[i][k]+y[k][j]);
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=cnt;j++)
            x[i][j] = tmp[i][j];
}
void powM(int x)
{
    memcpy(B,A,sizeof(A));
    while(x)
    {
        if(x&1)
            floyd(A,B);
        x>>=1;
        floyd(B,B);
    }
}
void work()
{
    powM(N-1);
    printf("%d",A[mp[S]][mp[E]]);
}
int main()
{   read();
    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值