HDU 2680-Choose the best route

用到的算法:Dijkstra

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2680

题目大意:一个笨蛋要坐车去朋友家,但坐车呕吐,所以想在最短时间内到达。

测试数据意思:

第一行三个数:n(车站的个数,n<1000)  |  m(代表车站之间所有线路的总个数m<200000)  | s(代表离朋友家最近的车站,即目的地)

下面有m行:   p q t   意思是:一条从p到q的线路,花费t时间

m行之后有个数字:w (代表可以在开始时搭乘的车站)

下面w个数W1,W2....Ww就是车站的编号

思路

从题意看,我们可以反过来看。看成是:从要到达的车站s到可以搭车的车站w ...反正不管正着看,还是反着看,就是求点到点的最短路径问题,而且没有负权值(为什么要没有负权值呢,下面讲),明显用dijkstra(混蛋,不懂的话,看下数据结构书p187)。

思路:你想啊笨蛋

如果存在一条从i到j的最短路经(pi,.......pk,pj),那么(p1.......pk)必然是一条从i到k的最短路经,否则的话,(pi.....pk,pj)不可能成为从i到j的最短路径。为了求出最短路径,dijkstra提出了一个按路径长度递增的次序产生最短路径的算法;

什么意思呢? 大致意思就是:对于原顶点V0,首先选择其直接相邻的顶点中最短的顶点Vi,那么可知:由V0经过Vi到与Vi直接相邻的顶点Vj的最短距离dis[j] = min(    map[V0][j] ,    dis[i] + map[i][j]) 这句最重要了,什么意思呢?就是要么直接到,要么转一下再到。(觉得很熟悉,在动态规划或贪心时,见过)

所以:

假设存在G=<V,E>,源顶点为V0,U={V0},dis[i]记录V0到i的最短距离 ,map[i][j]记录i到j的距离

1.从V-U中dis[i]值最小的顶点i,将i加入到U中;

2.更新与i直接相邻顶点的dis值。(dist[j]=min{map[V0][j],dis[i]+map[i][j]})

3.知道U=V,停止。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 2002    //这个地方太坑爸爸了,按照题目上我设成20002居然没执行函数体,直接return 啦
#define N 1002    //这些看题目数据范围
#define INF 999999
int n,m,s;
int map[M][M];    //map[i][j]表示i到j的权值
int dis[N];        //dis[i] 表示原点s到i的距离
int visit[N];      //顶点是否被访问过
void dijkstra(int s)
{
    int i,j,k = 0;
    int min;
    memset(visit,0,sizeof(visit));  //初始时都没被访问过
    for(i = 1; i <=n; i++)
    {
        dis[i] = map[s][i];
    }
    visit[s] = 1;  //第一个顶点即原点,被访问
    dis[s] = 0; //s到s 即为0
    for(i = 1; i < n; i++)
    {
        min = INF;
        //找到最小的
        for (j = 1; j <= n; j++)
        {
            if ( !visit[j] && dis[j] < min)
            {
                min = dis[j];
                k = j;   //最小的顶点
            }
        }
        if (min == INF)
        {
            break;
        }
        visit[k] = 1; //k顶点被访问
        //这里就是比较:直接到还是转一下再到
        //我猜,你个笨蛋肯定会问:不能转多下吗?
        //答曰:从源点一个一个向外扩展,具有最优子结构。每个dis[i]都保留着从s到i的最短距离。转多下的情况把它分解开,就知道了
        for(j = 1; j <= n; j++)   
        {
            if ( !visit[j] && dis[j] > dis[k] + map[k][j])
            {
                dis[j] = dis[k] + map[k][j];
            }
        }
    }
    return ;
}
int main()
{
    int i,j;
    int p,q,t,w;
    int  minx ,ww;
    while ( scanf("%d %d %d",&n,&m,&s) != EOF)
    {
        memset(dis,0,sizeof(dis));
        for (i = 1; i<= n; i++)
            for(j = 1; j <= n; j ++)
            {
                map[i][j] = INF;
            }
        for(i = 1; i <= m; i++)
        {
            scanf("%d%d%d",&p,&q,&t);
            if(t < map[q][p])     //题目上明明说的t <= 1000的,尼玛不加就错。以后保险起见要加啊
                map[q][p] = t;   //注意啊啊啊啊啊啊,因为是把S(目的地)看成源点,所以有向图方向要反过来啊
        }
        dijkstra(s);
        scanf("%d",&w);
        minx = INF;

        for (i = 1; i <=w; i++ )  //找到最小的dis
        {
            scanf("%d",&ww);
            if (minx > dis[ww])
            {
                minx = dis[ww];
            }
        }
        if ( minx != INF)
        {
            printf("%d\n",minx);
        }
        else
        {
            printf("-1\n");
        }
    }
    return 0;
}

转自:http://blog.csdn.net/bill_ming/article/details/7578037#



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值