• hdu1385 Minimum Transport Cost (floyd + 记录路径)


    题意: 给定一个有向图,有路径(边)权值和节点的权值,求一个字典序最小的最短路径(中间节点权值+路径权值)

    分析:此题目需要对floyd算法有比较深的了解,首先floyd是一个不断拓展路径的过程,同时也是不断增加中间节点的过程,所以累加中间节点权值的部分很好处理。

    理解了floyd 算法的过程之后,记录路径也非难事,关键是题目要求的是字典序最小的最短路径。

    记录路径有俩种方式:

    1)

    path[i][j] 记录 起点为i ,终点为j 的路径上j 的直接前驱

    View Code
    void init(int n)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&g[i][j]);
                if(i!=j && g[i][j]!=-1)
                    path[i][j]=i;
                else path[i][j]=-1;
            }
        for(int i=1;i<=n;i++)
            scanf("%d",&b[i]);
    }
    void floyd(int n)
    {
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
            {
                if(i==k || g[i][k]==-1) continue;
                for(int j=1;j<=n;j++)
                {
                    if(i==j || j==k) continue;
                    if(g[k][j]==-1)
                        continue;
                    int newdis=g[i][k]+g[k][j]+b[k];//k为中间节点
                    if(g[i][j]==-1 || g[i][j]>newdis)
                    {
                        g[i][j]=newdis;
                        path[i][j]=path[k][j];
                    }
                    else if(g[i][j]==newdis && path[i][j]>path[k][j])
                        path[i][j]=path[k][j];
                }
            }
    }

    事实上,上面这种方法并不能保证字典序,但不能否定的是,这是一种记录路径的方法
    为什么不能保证字典序呢?看一下更新路径的代码:

    if(g[i][j]==newdis && path[i][j]>path[k][j])
                        path[i][j]=path[k][j];

    更新的是j的直接前驱,其实也很明显了,单纯比较j 的直接并不能保证字典序的大小。。因为i的直接后继才是关键

    2)

    path[i][j] 记录起点为i ,中终点为j 的路径上i 的直接后继

    View Code
    inline void init(int n)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&g[i][j]);
                path[i][j]=j;
            }
        for(int i=1;i<=n;i++)
            scanf("%d",&b[i]);
    }
    inline void floyd(int n)
    {
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
            {
                if(i==k || g[i][k]==-1) continue;
                for(int j=1;j<=n;j++)
                {
                    if(i==j || j==k) continue;
                    if(g[k][j]==-1)
                        continue;
                    int newdis=g[i][k]+g[k][j]+b[k];
                    if(g[i][j]==-1 || g[i][j]>newdis)
                    {
                        g[i][j]=newdis;
                        path[i][j]=path[i][k];
                    }
                    else if(g[i][j]==newdis && path[i][j]>path[i][k])
                        path[i][j]=path[i][k];
                }
            }
    }

    这种方法就保证了字典序,因为每次拓展路径的时候都是保证起点的直接后继字典序,这样的路径拼接的时候才能保证字典序最小

    看完整的代码吧,还有要注意的一点是,数据中有可能出现起点等于终点的情况

    View Code
    #include<iostream>
    #include<algorithm>
    #include<stack>
    using namespace std;
    const int N = 50+10;
    int path[N][N],g[N][N];
    int b[N];
    inline void init(int n)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&g[i][j]);
                path[i][j]=j;
            }
        for(int i=1;i<=n;i++)
            scanf("%d",&b[i]);
    }
    inline void floyd(int n)
    {
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
            {
                if(i==k || g[i][k]==-1) continue;
                for(int j=1;j<=n;j++)
                {
                    if(i==j || j==k) continue;
                    if(g[k][j]==-1)
                        continue;
                    int newdis=g[i][k]+g[k][j]+b[k];
                    if(g[i][j]==-1 || g[i][j]>newdis)
                    {
                        g[i][j]=newdis;
                        path[i][j]=path[i][k];
                    }
                    else if(g[i][j]==newdis && path[i][j]>path[i][k])
                        path[i][j]=path[i][k];
                }
            }
    }
    inline void printpath(int s,int t)
    {
        printf("Path: %d",s);
        while(path[s][t]!=t)
        {
            printf("-->%d",path[s][t]);
            s=path[s][t];
        }
        printf("-->%d\n",t);
    }
    int main()
    {
        int n,s,t;
        while(scanf("%d",&n)==1 && n)
        {
            init(n);
            floyd(n);
            while(scanf("%d %d",&s,&t)==2)
            {
                if(s==-1 && t==-1)
                    break;
                printf("From %d to %d :\n",s,t);
                if(s==t)
                {
                    printf("Path: %d\n",s);
                    printf("Total cost : 0\n\n");
                    continue;
                }
                printpath(s,t);
                printf("Total cost : %d\n\n",g[s][t]);
            }
        }
        return 0;
    }
  • 相关阅读:
    类型"ImportMeta"上不存在属性"env"(Property 'env' does not exist on type 'ImportMeta')
    微信小程序开发常见问题
    js构建沙箱环境sandbox
    js 判断两个对象是否相同
    js判断类型方法
    初学knockoutjs记录4——Computed observables依赖监控(1 Using computed observables使用计算监控属性)
    初学knockoutjs记录3——Observables监控属性(2 Observable Arrays 监控数组)
    初学knockoutjs记录2——Observables监控属性(1 创建带有监控属性的view model)
    初学knockoutjs记录1——入门介绍及下载安装
    C、C++、JAVA编译器是如何处理未经初始化的变量的。
  • 原文地址:https://www.cnblogs.com/nanke/p/2484719.html
Copyright © 2020-2023  润新知