典型的dijkstra HDU 2680 Choose the best route

來源:互聯網
上載者:User

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=2680

題目大意:一個笨蛋要坐車去朋友家,但坐車嘔吐,所以想在最短時間內到達。

測試資料意思:

第一行三個數:n(車站的個數,n<1000)  |  m(代表車站之間所有線路的總個數)
 | 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 999999int 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;}

然後解釋:為什麼dijkstra為什麼不能有負權值???

比如:相鄰頂點i,j這兩個頂點之間的邊map[i][j] < 0,那麼當V0到Vi的最短路徑為dis[i]時,那dis[j]怎麼算呢?

按我們的dijkstra演算法該是dist[j]=min{map[V0][j],dis[i]+map[i][j]}那麼明顯,當計算dis[i]時,i頂點已經加入了最短路徑的集合U中,U中的最短路徑及其長度是不會再變更的,因為visit[i]
已經被訪問過變為1。。。dis[i] 再加上一個負數是小於dis[i]的,而算dis[i]時有可能該有j再到i的(這裡具體解釋一下:按自己理解,把擴充i,j之前所有的頂點看成a,若a到i權值為10,a到j權值為20,i到j權值為-100,那麼接下來a必定要先到i得到dis[i],這之後dis[i]就不能再更改了,然後再由{a,i}到j,但這時會發現:如果由a到j再到i,那麼dis[i]會更小,所以這裡就出現了錯誤。再所以,就不能有負權值啊)。

那如果有負權值,怎麼搞呢?

可以用    Bellman-Ford演算法。


如何儲存路徑呢?

倒過來想就好了,比如:從源點a經過很多很多步到達了b,然後經過一步到達了終點c,那麼此時我們反著想,倒著走,如果滿足

dis[b]+Cost[b][c])==dis[c]這個條件,就說明b必然在路徑中。。。。

看代碼比較清楚點。。。。

#include <cstdio>#include <iostream>#define BIG 5#include <cstring>#define MIN(a,b) ((a)<(b)?(a):(b))#define N 6#define INF 1000005using namespace std;int q[N+1],dis[N+1],vis[N+1],a,b,cnt;int Cost[N+1][N+1]={    {0,0,0,0,0,0},    {0,0,20,15,INF,INF,INF},    {0,2,0,INF,INF,10,30},    {0,INF,4,0,INF,INF,10},    {0,INF,INF,INF,0,INF,INF},    {0,INF,INF,INF,15,0,INF},    {0,INF,INF,INF,4,10,0}};//int sp[10];int index = 0;void dijstra(int u){    memset(vis,0,sizeof(vis));    for (int i=0; i<=N; i++)        dis[i]=INF;    dis[u]=0;    vis[u] =0;    for (int i=1; i<N; i++)    {        int _MIN=INF;        for (int j=1; j<=N; j++)            if (!vis[j] && dis[j]<_MIN)            {                _MIN=dis[j];                u=j;            }        vis[u]=1;        for (int j=1; j<=N; j++)            if (!vis[j] && dis[j]>dis[u]+Cost[u][j])                dis[j]=dis[u]+Cost[u][j];    }}void Getpath(int u){    q[++cnt]=u;    if (u==a)    {        for (int i=cnt; i>1; i--)        {            cout<<q[i]<<"->";           // sp[index++] = q[i];        }        cout<<q[1]<<endl;        cnt--;        return ;    }    for (int i=1; i<=N; i++)        if (i!=u  && dis[i] < INF && (dis[i]+Cost[i][u])==dis[u])        {            Getpath(i);        }    cnt--;}int main(){    printf("輸入起點和終點:\n");    while (scanf("%d%d",&a,&b)!=EOF)    {        //sp[1] = a;        cnt=0;        if(a == b)        {            printf("0\n");            continue ;        }        dijstra(a);          if(dis[b] >= INF)        {            printf("無路可走~\n");            continue;        }        Getpath(b);    }    return 0;}





聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.