杭电1385————Dijkstra+输出路径

Minimum Transport Cost

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 7701    Accepted Submission(s): 1985


Problem Description
These are N cities in Spring country. Between each pair of cities there may be one transportation track or none. Now there is some cargo that should be delivered from one city to another. The transportation fee consists of two parts: 
The cost of the transportation on the path between these cities, and

a certain tax which will be charged whenever any cargo passing through one city, except for the source and the destination cities.

You must write a program to find the route which has the minimum cost.
 

Input
First is N, number of cities. N = 0 indicates the end of input.

The data of path cost, city tax, source and destination cities are given in the input, which is of the form:

a11 a12 ... a1N
a21 a22 ... a2N
...............
aN1 aN2 ... aNN
b1 b2 ... bN

c d
e f
...
g h

where aij is the transport cost from city i to city j, aij = -1 indicates there is no direct path between city i and city j. bi represents the tax of passing through city i. And the cargo is to be delivered from city c to city d, city e to city f, ..., and g = h = -1. You must output the sequence of cities passed by and the total cost which is of the form:
 

Output
From c to d :
Path: c-->c1-->......-->ck-->d
Total cost : ......
......

From e to f :
Path: e-->e1-->..........-->ek-->f
Total cost : ......

Note: if there are more minimal paths, output the lexically smallest one. Print a blank line after each test case.

 

Sample Input
5 0 3 22 -1 4 3 0 5 -1 -1 22 5 0 9 20 -1 -1 9 0 4 4 -1 20 4 0 5 17 8 3 1 1 3 3 5 2 4 -1 -1 0
 

Sample Output
From 1 to 3 : Path: 1-->5-->4-->3 Total cost : 21 From 3 to 5 : Path: 3-->4-->5 Total cost : 16 From 2 to 4 : Path: 2-->1-->5-->4 Total cost : 17

题目大意如下:

有很多站点,站点与站点之间有道路相连,每次经过一个站点(起点与终点除外),需要缴费。

问从一个站点到另一个站点的最短路径,并且输出最短路径。如果有多个路径,输出字典序最小的那条。

可以简化成这样一个题目,求边权和顶点权的最短路。

这个不是问题,难点在于,如何存储路径以及如何按照字典序输出路径?


这里,我们引入一个pre数组,pre[i] = k 表示的是i节点的前驱是k。

在最短路更新的过程中(Dijkstra算法)

如果在更新时,有多条最短路,那么就比较这两条路哪个字典序更小(即保证pre数组中存储的一直都是字典序最小的那条路),比较的方法是用一个compare函数进行的。

bool compare(int now,int ever)//将两条不同的路径转换为字符串直接比较字典序
{
    char s1[maxn],s2[maxn];

    memset(s1,0,sizeof(s1));
    memset(s2,0,sizeof(s2));
    //获取现在的路径
    pos = 0;//全局变量
    dfs(now,s1);
    s1[pos] = '\0';
    //获取原来结点的路径
    pos = 0;
    dfs(ever,s2);
    s2[pos] = '\0';

    if(strcmp(s1,s2)  == 1)//如果说s1比s2的字典序大的话,选择原来的路径
        return true;
    return false;
}

now是当前更新到的结点,ever是之前的最短路。(即start —— ever的费用和start——now的费用是一样的)

此时进行比较,既然是比较他们的字典序,那么就把路径转换成一个字符串(dfs函数)直接进行字符串的比较就可以了。

完整代码如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 2005
#define INF 0xffffff
using namespace std;

int G[maxn][maxn];
int pre[maxn];//pre[i] = k 表示i的前驱是k
int tax[maxn];//每一个结点的税
int N,pos = 0;//pos变量在compare中有说明

int Min(int x,int y)
{
    return (x < y) ? x : y;
}
void init()
{
    for(int i = 0 ; i <= N ; ++i)
        for(int j = 0 ; j <= N ; ++j)
            G[i][j] = INF;
    memset(tax,0,sizeof(tax));//税收
}
void dfs(int node,char *s)
{
    if(node == -1)//结束表示path[node] = -1是递归出口
        return ;
    dfs(pre[node],s);
    s[pos++] = node + '0';//将结点序号转换为字符串
}
bool compare(int now,int ever)//将两条不同的路径转换为字符串直接比较字典序
{
    char s1[maxn],s2[maxn];

   // printf("now :%d ever:%d\n",now,ever);
    memset(s1,0,sizeof(s1));
    memset(s2,0,sizeof(s2));
    //获取现在的路径
    pos = 0;
    dfs(now,s1);
    s1[pos] = '\0';
    //获取原来的路径
    pos = 0;
    dfs(ever,s2);
    s2[pos] = '\0';

    if(strcmp(s1,s2)  == 1){//如果说s1比s2的字典序大的话,选择原来的路径
        //printf("%s %s\n",s1,s2);
        return true;
    }
    return false;
}
int Dijkstra(int s,int e)
{
    int intree[maxn];
    int mincost[maxn],cost;
    int node;

    memset(intree,0,sizeof(intree));
    memset(pre,0,sizeof(pre));
    intree[s] = 1;
    for(int i = 1 ; i <= N ; ++i){
        mincost[i] = G[s][i];
        if(G[s][i] != INF)
            pre[i] = s;
        else
            pre[i] = -1;
    }
    for(int nodenum = 1 ; nodenum <= N-1 ; ++nodenum){
        cost = INF;
        for(int i = 1; i <= N ; ++i){
            if(!intree[i] && mincost[i] < cost){
                cost = mincost[i];
                node = i;
            }
        }
        intree[node] = 1;
        for(int i = 1 ; i <= N ; ++i)
            if(!intree[i]){
                int temp = mincost[node] + G[node][i];
                if(mincost[i] > temp){
                    mincost[i] = temp;
                    pre[i] = node ;
                }
                else if(mincost[i] == temp && compare(i,node))
                    pre[i] = node;

            }
    }
    return mincost[e] - tax[e];//终点不用交税
}
void update()
{
    for(int i = 1 ; i <= N ; ++i)
        for(int j = 1 ; j <= N ; ++j)
            if(G[i][j] != INF)
                G[i][j] += tax[j];//从i -> j 需要支付税,那么无形中费用相当于增加了
}
void print_path(int s,int e)
{
    int path[maxn];
    int cnt = 1;

    memset(path,0,sizeof(path));
    path[cnt++] = e;//终止点,倒序输出即可
    int last = pre[e];
    while(last != s){
        path[cnt++] = last;
        last = pre[last];
    }
    path[cnt] = s;
    for(int i = cnt ; i >= 1 ; --i)
        if(i == 1)
            printf("%d\n",path[i]);
        else
            printf("%d-->",path[i]);
}//这个函数也可以用递归或者栈来重写
int main()
{
    int cost;
    int s,e;//start,end
    while(scanf("%d",&N) != EOF){
        if(N == 0)
            break;
        init();
        for(int i = 1 ; i <= N ; ++i)
            for(int j = 1 ; j <= N ; ++j){
                scanf("%d",&cost);
                if(cost > 0) G[i][j] = cost;//G[i][j] == -1 || G[i][j] == 0 表示不连通
            }
        for(int i = 1 ; i <= N ; ++i)
            scanf("%d",&tax[i]);
        update();//更新实际的费用,道路费用+tax
        while(scanf("%d%d",&s,&e) != EOF){
            if(s == -1 && e == -1)
                break;
            if(s == e){//起点和终点重合的时候
                printf("From %d to %d :\n",s,e);
                printf("Path: %d\n",s);
                printf("Total cost : %d\n\n",0);
            }
            else{
                int totalcost = Dijkstra(s,e);
                printf("From %d to %d :\n",s,e);
                printf("Path: ");
                print_path(s,e);
                printf("Total cost : %d\n\n",totalcost);
            }
        }
    }
    return 0;
}

posted @ 2014-11-23 13:39  SixDayCoder  阅读(275)  评论(0编辑  收藏  举报