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;
}

 

 

posted @ 2012-05-05 14:21  枕边梦  阅读(412)  评论(2编辑  收藏  举报