floyd + 记录路径 hdu 1385

floyd算法:
用于求解全源最短路径问题,也就是任意两点间的最短路径。
定义d[k][i][j]为只使用0~k的情况下以i为起点到j的最短路径。
将0~k的情况归约到0~k-1的情况。当0~k时,分为两种情况一种是只经过k一次,另一种是不经过k。若不经过k,那么d[k][i][j] = d[k-1][i][j]。如果经过k,那么d[k][i][j] = d[k-1][i][k]+d[k-1][k][j]。
转移方程:d[k][i][j] = min(d[k-1][i][j],d[k-1][i][k]+d[k-1][k][j])。
联想到背包问题的做法,我们可以直接使用二维数组来存储,不断进行d[i][j] = min(d[i][j],d[i][k]+d[k][j])的更新。
floyd中的记录路径:

记录前驱:

在dijkstra中,记录路径一般用一个pre[]数组来记录每个点的前驱,在更新最短路径的同时,更新前驱。
floyd中也可以这样,用pre[a][b]来表示起点为a的情况下,b点的前驱。初始化就遍历i=0->n,p[i][j] = i。再将p[i][i]赋为-1,方便还原路径。

记录后继:

但是有点不同的就是也可以用p[a][b]来记录以b为终点的情况下,a点的后继。floyd可以记录后继的,因为这样定义以后,如果要经过k点,那么p[i][j]就应该被p[i][k]所更新,也就是说以b为终点时,a点的后继其实也就是以k为终点时,a点的后继。于是就将初始化改成i=0->n ,p[i][j] = j。也将p[i][i]赋为-1,方便还原路径。
hdu 1385
题意:给一个图,有多次询问,也就是要求任意两点之间的最短路径。并且将路径输出。如果有相同的最短路,输出字典序最小的(一开始就没有注意到这个orzz)
背景:采用记录前驱的方法保存路径,交了,WA,看了好久才发现要字典序,所以在d[i][j] = d[i][k]+d[k][j]的时候要小心,有可能需要更新路径,虽然最短路没有改变。
发现记录前驱的话字典序一直无法满足,因为只能保证前驱尽可能的小,但是字典序应该是从头开始,每个数都尽可能小。
discuss里一组数据:
3
0 2 4
-1 0 1
-1 -1 0
1 1 1
1 3
结果是1–>2–>3
记录前驱的话结果会是1–>3。
于是看了别人的学习了下发现还可以记录后继orz。在输出的时候就直接顺序输出就好了。如果是记录前驱的话可以用一个栈来先保存,最后将栈中的一个个输出。

//floyd
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
#define M 1000
#define INF 0x3f3f3f3f
int d[M][M];
int p[M][M];//p[i][j] 表示以j为终点的情况下,i的后继。
int v[M];
int n;
void floyd()
{
    for(int k = 1;k <= n;k++)
    for(int i = 1;i <= n;i++)
    for(int j = 1;j <= n;j++)
    {
        if(d[i][j] > d[i][k]+d[k][j]+v[k]) //经过k点需要交v[k] 这么多税
        {
            d[i][j] = d[i][k]+d[k][j]+v[k];
            p[i][j] = p[i][k];
        }
        else if(d[i][j] == d[i][k]+d[k][j]+v[k])
            p[i][j] = min(p[i][j],p[i][k]);
    }
}
int main()
{
    while(scanf("%d",&n)==1 && n)
    {
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= n;j++)
            {
                p[i][j] = j;  //将以j为终点的后继都初始化为j
                scanf("%d",&d[i][j]);
                if(d[i][j] == -1) d[i][j] = INF;
            }
        }
        int a,b;
        for(int i = 1;i <= n;i++)
        {
            p[i][i] = -1;
            scanf("%d",&v[i]);
        }
        floyd();
        while(scanf("%d %d",&a,&b)==2)
        {
            if(a==-1 && b==-1) break;
            printf("From %d to %d :\n",a,b);
            printf("Path: ");
            int i = p[a][b];
            printf("%d",a);
            while(i != -1)
            {
                printf("-->%d",i);
                i = p[i][b];
            }
            printf("\nTotal cost : %d\n\n",d[a][b]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值