【最短路】【Dijkstra算法】【模板】讲解 + 例题1 HDU 2544 最短路 + 例题2 HDU - 1224 Free DIY Tour 【旅游飞行,使有趣值最大(无边权,有点权)】

【最短路】【Dijkstra算法】【模板】讲解 + 例题 HDU 2544 最短路


Dijkstra算法讲解

  • 适用情况: 单源多汇最短路(边权为正)
  • 复杂度: O(E+ VlogV) 【堆优化后】
  • 思想: 贪心

    • D(s, t) = {Vs … Vi … Vj … Vt}表示s到t的最短路,其中i和j是这条路径上的两个中间结点,那么D(i, j)必定是i到j的最短路,这个性质是显然的,可以用反证法证明。
    • 基于上面的最优子结构性质,如果存在这样一条最短路D(s, t) = {Vs … Vi Vt},其中i和t是最短路上相邻的点,那么D(s, i) = {Vs … Vi} 必定是s到i的最短路。Dijkstra算法就是基于这样一个性质,通过最短路径长度递增,逐渐生成最短路。
    • 如果源点到x点的最短路已经求出,并且保存在d[x] ( 可以将它理解为D(s, x) )上,那么可以利用x去更新 x能够直接到达的点 的最短路。即:d[y] = min{ d[y], d[x] + w(x, y) }
  • 算法实现
    与最小生成树的Prim算法非常相似

//伪代码
设起始点为s
清除所有点的标记
设dis[s] = 0,其他dis[i] = INF
循环n次
{ 
   在所有未标记的节点中,选出dis值最小的节点X
   给节点X标记
   对于从X出发可以到达的点y,更新dis[y] = min{dis[y], dis[x]+w(x,y)}//只有此处与Prim算法不同:对于从X出发可以到达的,且不在当前集合中的点y,更新dis[y] = min{dis[y],w(x,y)}
}
  • 实现过程:

    • 我们把已经处理出来源点到i点最短距离的点放在一个集合里,叫做最短路点集。
    • 每次先处理出不在最短路点集的所有点(即!vis)到目前的最短路点集(即vis)的距离dis[],然后从中找到距离最小的点X,这个点一定在最短路上,然后把X加入到最短路点集中
    • 这时,我们得到了一个新的最短路点集,然后再次更新集合外的点到最短路点集的距离dis[]
    • 重复上述过程,直到所有点都加入到了最短路点集中
    • 图例:
      首先从根(也就是目标)出发,根标记为0,其他节点标记为∞,更新dis,即其他节点到0的距离,然后找到距离最小的,比如第一步2最小,标记2,然后从2往前走,更新其他节点到0 和 2 的距离,比如其相邻节点原来是5,现在由于2+2=4,故更新下,然后再比较所有节点里面,距离最小的是3,标记3,然后更新其他节点到0 ,2,3的距离,然后不断循环,这样一路一路从距离最小的节点往相邻走,最终达到收敛。
      Dijkstra
  • 和Bellman - Ford算法的比较

    • Dijkstra算法每一次都是从最短距离的节点出发,而Bellman算法每一次都是从所有点集中的点出发,向相邻点走
    • 1、Dijkstra算法每次将一个点X加入最短路点集后,其dis值就是最短路的长度了,就已经是最小的了,不会再改变了。
      2、而Bellman算法每次将一个点X的dis更新,把新点加入到队列里时,之前点的dis还是可能会被相邻点更新,因为它存储的只是一个暂时的可能最小值,还会受到后面点的影响。直到最后一次算法结束时,所有点的dis才会最终确定。
  • 模板:

【邻接矩阵实现,未优化版】

void dijkstra(int s)
{
    memset(dis, INF, sizeof(dis));
    for(int i = 1; i <= n; i++)//处理dis为到s的距离
    {
        dis[i] = mapp[s][i];
        vis[i] = 0;
    }
    vis[s] = 1;
    dis[s] = 0;
    for(int i = 1; i < n; i++)//执行n-1轮
    {
        int min_dis = INF;
        int from;
        for(int j = 1; j <= n; j++)//寻找所有集合外的点到集合距离最小的点
        {
            if(!vis[j] && min_dis > dis[j])
            {
                from = j;
                min_dis = dis[j];
            }
        }
        vis[from] = 1;
        for(int j = 1; j <= n; j++)//更新集合外点到集合的距离
        {
            if(!vis[j])
                dis[j] = min(dis[j], dis[from] + mapp[from][j]);
        }
    }
}

【优先队列优化版】

int dis[maxn];

struct Edge//边 
{
    int u, v, w;
    Edge() {};
    Edge(int uu, int vv, int ww): u(uu), v(vv), w(ww) {};
};

vector<Edge> edges;//边数组 
vector<int> G[maxn];//存储每个节点对应的边的序号 

void init(int nn)//清理 
{
    for(int i = 0; i <= nn; i++)
        G[i].clear();
    edges.clear();
}

void AddEdge(int uu, int vv, int ww)//加边 
{
    edges.push_back(Edge(uu, vv, ww));
    int edgenum = edges.size();
    G[uu].push_back(edgenum - 1);
}

struct node//优先队列优化,dis小的先出队 
{
    int u, d;
    node() {};
    node(int uu, int dd): u(uu), d(dd) {};
    friend bool operator < (node a, node b)
    {
        return a.d > b.d;
    }
};

void dijkstra(int s)
{
    priority_queue<node> q;
    memset(dis, INF, sizeof(dis));//dis初始化为INF 
    dis[s] = 0;
    q.push(node(s, dis[s]));
    while(!q.empty())
    {
        node cur = q.top();
        q.pop();
        int from = cur.u;
        if(cur.d != dis[from])//减少了vis数组,表示该节点被取出来过 
            continue;
        for(int i = 0; i < G[from].size(); i++)//更新所有集合外点到集合的dis 
        {
            Edge e = edges[G[from][i]];
            if(dis[e.v] > dis[e.u] + e.w)
            {
                dis[e.v] = dis[e.u] + e.w;
                pre[e.v] = from;//存储父节点 
                q.push(node(e.v, dis[e.v]));//将有更新的dis加入到队列中 
            }
        }
    }
}

例题1 HDU 2544 最短路

在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。

Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间

Sample Input
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0

Sample Output
3
2

AC代码:

#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <queue>
#include <vector>
#define INF 0x3f3f3f3f

using namespace std;

struct Edge
{
    int u, v, w;
    Edge() {};
    Edge(int uu, int vv, int ww): u(uu), v(vv), w(ww) {};
};

const int maxn = 105;
vector<Edge> edges;
vector<int> G[maxn];
int dis[maxn];

void init(int nn)
{
    for(int i = 0; i <= nn; i++)
        G[i].clear();
    edges.clear();
}

void AddEdge(int uu, int vv, int ww)
{
    edges.push_back(Edge(uu, vv, ww));
    int edgenum = edges.size();
    G[uu].push_back(edgenum - 1);
}

struct node
{
    int u, d;
    node() {};
    node(int uu, int dd): u(uu), d(dd) {};
    friend bool operator < (node a, node b)
    {
        return a.d > b.d;
    }
};

void dijkstra(int s)
{
    priority_queue<node> q;
    memset(dis, INF, sizeof(dis));
    dis[s] = 0;
    q.push(node(s, dis[s]));
    while(!q.empty())
    {
        node cur = q.top();
        q.pop();
        int from = cur.u;
        if(cur.d != dis[from])
            continue;
        for(int i = 0; i < G[from].size(); i++)
        {
            Edge e = edges[G[from][i]];
            if(dis[e.v] > dis[e.u] + e.w)
            {
                dis[e.v] = dis[e.u] + e.w;
                q.push(node(e.v, dis[e.v]));
            }
        }
    }
}

int main()
{
    int n, m;
    while(~scanf("%d%d", &n, &m) && n && m)
    {
        init(n);
        for(int i = 0; i < m; i++)
        {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            AddEdge(u, v, w);
            AddEdge(v, u, w);
        }
        dijkstra(1);
        printf("%d\n", dis[n]);
    }
    return 0;
}

例题2 HDU - 1224 Free DIY Tour

Weiwei is a software engineer of ShiningSoft. He has just excellently fulfilled a software project with his fellow workers. His boss is so satisfied with their job that he decide to provide them a free tour around the world. It’s a good chance to relax themselves. To most of them, it’s the first time to go abroad so they decide to make a collective tour.

The tour company shows them a new kind of tour circuit - DIY circuit. Each circuit contains some cities which can be selected by tourists themselves. According to the company’s statistic, each city has its own interesting point. For instance, Paris has its interesting point of 90, New York has its interesting point of 70, ect. Not any two cities in the world have straight flight so the tour company provide a map to tell its tourists whether they can got a straight flight between any two cities on the map. In order to fly back, the company has made it impossible to make a circle-flight on the half way, using the cities on the map. That is, they marked each city on the map with one number, a city with higher number has no straight flight to a city with lower number.

Note: Weiwei always starts from Hangzhou(in this problem, we assume Hangzhou is always the first city and also the last city, so we mark Hangzhou both 1 and N+1), and its interesting point is always 0.

Now as the leader of the team, Weiwei wants to make a tour as interesting as possible. If you were Weiwei, how did you DIY it?

Input
The input will contain several cases. The first line is an integer T which suggests the number of cases. Then T cases follows.
Each case will begin with an integer N(2 ≤ N ≤ 100) which is the number of cities on the map.
Then N integers follows, representing the interesting point list of the cities.
And then it is an integer M followed by M pairs of integers [Ai, Bi] (1 ≤ i ≤ M). Each pair of [Ai, Bi] indicates that a straight flight is available from City Ai to City Bi.

Output
For each case, your task is to output the maximal summation of interesting points Weiwei and his fellow workers can get through optimal DIYing and the optimal circuit. The format is as the sample. You may assume that there is only one optimal circuit.

Output a blank line between two cases.

Sample Input
2
3
0 70 90
4
1 2
1 3
2 4
3 4
3
0 90 70
4
1 2
1 3
2 4
3 4

Sample Output
CASE 1#
points : 90
circuit : 1->3->1

CASE 2#
points : 90
circuit : 1->2->1

题意:
旅行,起点为1,终点仍为1(在输入数据中用n+1表示,在输出中仍需变回1),航班可以从小序号飞往大序号,不可以从大序号飞往小序号,每个景点都有有趣值,要求从1出发,再回到1(即n+1)的路径,要求使有趣值最大。

思路:
变为点权的最长路,搜索从1到n+1的最长路,用pre记录父节点,然后逆序输出

AC代码:

#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <queue>
#include <vector>
#define INF 0x3f3f3f3f

using namespace std;

const int maxn = 105;
int w[maxn], dis[maxn], pre[maxn];
vector<int> G[maxn];
int t, n, m;

struct node
{
    int u, d;
    node() {};
    node(int uu, int dd): u(uu), d(dd) {};
    friend bool operator < (node a, node b)
    {
        return a.d < b.d;
    }
};

void dijkstra(int s)
{
    priority_queue<node> q;
    memset(dis, 0, sizeof(dis));
    memset(pre, -1, sizeof(pre));
    dis[s] = 0;
    pre[s] = -1;
    q.push(node(s, 0));
    while(!q.empty())
    {
        node cur = q.top();
        q.pop();
        int from = cur.u;
        if(dis[from] != cur.d)
            continue ;
        for(int i = 0; i < G[from].size(); i++)
        {
            int to = G[from][i];
            if(dis[to] < dis[from] + w[to])
            {
                dis[to] = dis[from] + w[to];
                pre[to] = from;
                q.push(node(to, dis[to]));
            }
        }
    }
}

void pri(int e)
{
    if(pre[e] != -1)
        pri(pre[e]);
    if(e != n + 1)
        printf("%d->", e);
    else
        printf("1\n");
}

int main()
{
    scanf("%d", &t);
    for(int kase = 1; kase <= t; kase++)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n + 1; i++)
            G[i].clear();
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &w[i]);
        }
        w[n + 1] = 0;
        scanf("%d", &m);
        for(int i = 1; i <= m; i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
        }
        dijkstra(1);
        if(kase != 1)
            printf("\n");
        printf("CASE %d#\n", kase);
        printf("points : %d\n", dis[n + 1]);
        printf("circuit : ");
        pri(n + 1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Floraqiu/article/details/81484870