07-图6 旅游规划 (Dijkstra,堆优化的Dijkstra算法,Dijkstra+DFS)

07-图6 旅游规划

分数 25

作者 陈越

单位 浙江大学

有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。

输入格式:

输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0~(N−1);M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。

输出格式:

在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。

输入样例:

4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

输出样例:

3 40

1) 朴素 Dijkstra 算法:

/**
    1) 朴素 Dijkstra 算法:
*/


/**
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;

struct ENode
{
    int v, dis,wei;
};

const int INF = 0x3fffffff; //无穷大数据
const int maxn=510; //顶点最大值
vector<ENode> Adj[maxn];  //邻接表
int d[maxn];  //最短路径
int c[maxn];  // 路径最小消耗量
bool hs[maxn]; // 顶点是否被标记

int Nv,Ne,st,ed; //顶点数,边数,起点,终点

void read(); //读出数据,并建图
void Dijkstra(int s); //Gijkstra 算法

int main()
{
    read();
    Dijkstra(st);
    cout << d[ed] << ' ' << c[ed] << endl;
    return 0;
}

void read()  //读出数据,并建图
{
    cin >> Nv >> Ne >> st >> ed;
    for(int i=0;i<Ne;++i)
    {
        int v1,v2,dis,wei;
        cin >> v1 >> v2 >> dis >> wei;
        Adj[v1].push_back({v2,dis,wei});
        Adj[v2].push_back({v1,dis,wei});
    }
}

void Dijkstra(int s) //Gijkstra 算法
{
    fill(d,d+maxn,INF); //初始化所有值为无穷大
    memset(c,0,sizeof(c));  //初始化所有值为0;
    d[s]=0; //起点距离赋为0

    for(int i=0;i<Nv;++i)
    {
        int u=-1 , MIN=INF;
        for(int j=0;j<Nv;++j)  //每次寻找还未选择的离起始点最短距离的顶点编号
        {
            if(hs[j]==0 && d[j]<MIN)
            {
                u=j;
                MIN=d[j];
            }
        }

        if(u==-1)
            return;
        hs[u]=1;   //将选择的顶点设置为已标记
        for(int j=0;j<Adj[u].size();++j)
        {
            int v=Adj[u][j].v; //记得取顶点下标
            if(hs[v]==0 && d[u]+Adj[u][j].dis < d[v])
            {
                d[v]=d[u]+Adj[u][j].dis;
                c[v]=c[u]+Adj[u][j].wei;
            }
            else if(hs[v]==0 && d[u]+Adj[u][j].dis == d[v])
            {
                if(c[u]+Adj[u][j].wei < c[v])
                    c[v]=c[u]+Adj[u][j].wei;
            }
        }
    }
}
*/

2) 堆优化Dijkstra 算法:

/**
    2) 堆优化Dijkstra 算法:
*/

/**
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> PII;

const int maxn = 510,INF = 1e9;
struct ENode
{
    int d,w;


    ENode()
    {
        d=INF;
    }

    ENode(int _d,int _w)
    {
        d=_d;
        w=_w;
    }


};


ENode G[maxn][maxn];
int d[maxn];
int cost[maxn];
bool hs[maxn];
int Nv,Ne,st,ed;

void Read()
{
    cin >> Nv >> Ne >> st >> ed;
    for(int i=0;i<Ne;++i)
    {
        int u,v,d,w;
        cin >> u >> v >> d >> w;
        G[u][v]=G[v][u]={d,w};
    }
}

void Dijkstra()
{
    fill(d,d+maxn,INF);
    d[st]=0;
    priority_queue<PII,vector<PII>,greater<PII> > pq;
    pq.push({0,st});

    while(pq.size())
    {
        PII top = pq.top();
        pq.pop();
        int u=top.second;


        hs[u]=1;
        for(int v=0;v<Nv;++v)
        {
            int dis = G[u][v].d,w=G[u][v].w;
            if(d[u]+dis < d[v])
            {
                d[v]=d[u]+dis;
                cost[v]=cost[u]+w;
                pq.push({d[v],v});
            }
            else if(d[u]+dis == d[v])
                cost[v]=min(cost[v],cost[u]+w);
        }
    }

    cout << d[ed] << ' ' << cost[ed] << endl;
}

int main()
{
    Read();

    Dijkstra();

    return 0;
}
*/

3)朴素Dijkstra + DFS;


/**
    3)朴素Dijkstra + DFS;
*/

/**
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;

const int INF = 0x3fffffff; //无穷大数据
const int maxn=510; //顶点最大值
int G[maxn][maxn];  //邻接表
int cost[maxn][maxn];
int d[maxn];  //最短路径
int c[maxn];  // 路径最小消耗量
bool hs[maxn]; // 顶点是否被标记

int Nv,Ne,st,ed; //顶点数,边数,起点,终点

int minCost = INF;
vector<int> pre[maxn];
vector<int> path,tempPath;

void read(); //读出数据,并建图
void Dijkstra(int s); //Gijkstra 算法
void DFS(int u);

int main()
{
    read();
    Dijkstra(st);
    DFS(ed);    //DFS 得从终点开始,因为pre数组是存储的u的前驱节点

    cout << d[ed] << ' ' << minCost << endl;
    return 0;
}

void read()  //读出数据,并建图
{
    fill(*G,*G+maxn*maxn,INF);

    cin >> Nv >> Ne >> st >> ed;
    for(int i=0;i<Ne;++i)
    {
        int v1,v2,dis,wei;
        cin >> v1 >> v2 >> dis >> wei;
        G[v1][v2] = G[v2][v1] =dis;
        cost[v1][v2] = cost[v2][v1] = wei;
    }
}

void Dijkstra(int s) //Gijkstra 算法
{
    fill(d,d+maxn,INF); //初始化所有值为无穷大
    d[s]=0; //起点距离赋为0

    for(int i=0;i<Nv;++i)
    {
        int u=-1 , MIN=INF;
        for(int j=0;j<Nv;++j)  //每次寻找还未选择的离起始点最短距离的顶点编号
        {
            if(hs[j]==0 && d[j]<MIN)
            {
                u=j;
                MIN=d[j];
            }
        }

        if(u==-1)
            return;
        hs[u]=1;   //将选择的顶点设置为已标记
        for(int v=0;v<Nv;++v)
        {
            if(hs[v]==0 && d[u]+G[u][v] < d[v])
            {
                d[v]=d[u]+G[u][v];
                pre[v].clear();
                pre[v].push_back(u);
            }
            else if(hs[v]==0 && d[u]+G[u][v] == d[v])
            {
                if(c[u]+cost[u][v] < c[v])
                {
                    pre[v].push_back(u);
                }
            }
        }
    }
}

void DFS(int u)
{
    if(u == st)
    {
        tempPath.push_back(u);

        int value = 0;
        for(int i=tempPath.size()-1;i>0;--i)    //如果只是计算边权或者点权之和,那么正序计算或者逆序计算都可
        {
            int fs = tempPath[i],sc = tempPath[i-1];
            value += cost[fs][sc];
        }

        if(value < minCost)
        {
            minCost = value;
            path = tempPath;
        }

        tempPath.pop_back();
    }

    tempPath.push_back(u);
    for(int i=0;i<pre[u].size();++i)
    {
        int v = pre[u][i];
        DFS(v);
    }
    tempPath.pop_back();
}
*/

 4)堆优化版的Dijkstra算法 + DFS;


/**
    4)堆优化版的Dijkstra算法 + DFS;
*/

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int,int> PII;

const int INF = 0x3fffffff; //无穷大数据
const int maxn=510; //顶点最大值
int G[maxn][maxn];  //邻接表
int cost[maxn][maxn];
int d[maxn];  //最短路径
int c[maxn];  // 路径最小消耗量
bool hs[maxn]; // 顶点是否被标记

int Nv,Ne,st,ed; //顶点数,边数,起点,终点

int minCost = INF;
vector<int> pre[maxn];
vector<int> path,tempPath;

void read(); //读出数据,并建图
void Dijkstra(); //Gijkstra 算法
void DFS(int u);

int main()
{
    read();
    Dijkstra();
    DFS(ed);    //DFS 得从终点开始,因为pre数组是存储的u的前驱节点

    cout << d[ed] << ' ' << minCost << endl;
    return 0;
}

void read()  //读出数据,并建图
{
    fill(*G,*G+maxn*maxn,INF);

    cin >> Nv >> Ne >> st >> ed;
    for(int i=0;i<Ne;++i)
    {
        int v1,v2,dis,wei;
        cin >> v1 >> v2 >> dis >> wei;
        G[v1][v2] = G[v2][v1] =dis;
        cost[v1][v2] = cost[v2][v1] = wei;
    }
}



void Dijkstra()
{
    fill(d,d+maxn,INF);
    fill(c,c+maxn,INF);
    d[st]=0;
    c[st]=0;

    priority_queue<PII,vector<PII>,greater<PII> > pq;   //优先队列,first存储距离,second 存储点的编号
    pq.push({0,st});

    while(pq.size())
    {
        PII top = pq.top();
        pq.pop();
        int u=top.second;

        hs[u]=1;
        for(int v=0;v<Nv;++v)
        {
            int dis = G[u][v],w=cost[u][v];
            if(d[u]+dis < d[v])
            {
                d[v]=d[u]+dis;
                pq.push({d[v],v});
                pre[v].clear(); //如果途径u能使d[v]更短,那么说明除了u以外的前驱节点都不是最短路径
                pre[v].push_back(u);
            }
            else if(d[u]+dis == d[v])
            {
                pre[v].push_back(u);
            }
        }
    }
}

void DFS(int u)
{
    if(u == st)
    {
        tempPath.push_back(u);

        int value = 0;
        for(int i=tempPath.size()-1;i>0;--i)    //如果只是计算边权或者点权之和,那么正序计算或者逆序计算都可
        {
            int fs = tempPath[i],sc = tempPath[i-1];
            value += cost[fs][sc];
        }

        if(value < minCost)
        {
            minCost = value;
            path = tempPath;
        }

        tempPath.pop_back();
    }

    tempPath.push_back(u);
    for(int i=0;i<pre[u].size();++i)
    {
        int v = pre[u][i];
        DFS(v);
    }
    tempPath.pop_back();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值