Round15—最短路径

判断题:

1-1在一个有权无向图中,若ba的最短路径距离是12,且cb之间存在一条权为2的边,则ca的最短路径距离一定不小于10。(T)

解析: 我们首先来分析b->a有几种可能,首先是b到a有直接的路径,其次b通过其他的结点到达a点。

如果是b通过c点到达a点我们就可以知道,min{b->c}+min{c->a}>=12;

但是我们知道min{b->c}<=2,因此12<=min{b->c}+min{c->a}<=2+min{c->a};

我们可以知道min{c->a}>=10;

选择题:

2-1我们用一个有向图来表示航空公司所有航班的航线。下列哪种算法最适合解决找给定两城市间最经济的飞行路线问题? (1分)

  • Dijkstra算法

  • Kruskal算法
  • 深度优先搜索
  • 拓扑排序算法

解析:首先排除kruskal与拓扑排序,因为一个是最小生成树算法一个是拓扑排序(解决关键路径)。

如果对于路径无权图我们可以使用bfs,首先搜索到的就是最短的路径。

但是我们无法保证路径相等(无权就是每条路的代价相同),因此还是Dijkstra算法更优。

2-2数据结构中Dijkstra算法用来解决哪个问题? (1分)

  • 关键路径
  • 最短路径
  • 拓扑排序
  • 字符串匹配

解析:这个题白给。

2-3若要求在找到从S到其他顶点最短路的同时,还给出不同的最短路的条数,我们可以将Dijkstra算法略作修改,增加一个count[]数组:count[V]记录S到顶点V的最短路径有多少条。则count[V]应该被初始化为: (3分)

  • count[S]=1;对于其他顶点V则令count[V]=0
  • count[S]=0;对于其他顶点V则令count[V]=1
  • 对所有顶点都有count[V]=1
  • 对所有顶点都有count[V]=0

解析:首先这个题有点dp(动态规划的意思),首先count[V]表示s到v的最短路的条数。

那么我们考虑状态转移方程。

1.dis[x]>dis[y]+length[y][x];

也就是当v->x的一知道最短路径大于当前的路径我们要松弛,那么显然有几种方法到y就有几种方法到x。

count[x] = count[y];

2.dis[x]==dis[y]+length[y][x];

也就是出现了多组最优解,使得v到x的最短路相同。

那么到x的最短路径数目,就是原本的数目加上,到y的数目。

count[x] += count[y];

那么初始化是什么,首先我们不能保证这个图树连同图,首先除了s之外的结点肯定是0,那么s到s其实本身就是一种方法,因此count[s]=1;

2-4使用迪杰斯特拉(Dijkstra)算法求下图中从顶点1到其他各顶点的最短路径,依次得到的各最短路径的目标顶点是:(2分)

  • 5, 2, 3, 4, 6
  • 5, 2, 3, 6, 4
  • 5, 2, 4, 3, 6
  • 5, 2, 6, 3, 4

解析:按照dijkstra的思想,每次选择临时标记当中的最短路径。

2-5在一个有权无向图中,如果顶点b到顶点a的最短路径长度是10,顶点c与顶点b之间存在一条长度为3的边。那么下列说法中有几句是正确的? (3分)

  1. c与a的最短路径长度就是13
  2. c与a的最短路径长度就是7
  3. c与a的最短路径长度不超过13
  4. c与a的最短路径不小于7
  • 1句
  • 2句
  • 3句
  • 4句

解析:首先我们就可以排除第一个,如果c->a存在直接路径并且小于13.

因为min{b->a}=10;

那么min{b->c}+min{c->a}>=10;

同1-1我们知道第四个是正确的,同时我们可以判断min{c->a}<=13;

也就是当前已知一条长为13的最短路径,那么我们所找的最短路不能比他还大。

编程题:

7-1 城市间紧急救援 (25 分)

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。

第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。

输出格式:

第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。

输入样例:

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

输出样例:

2 60
0 1 3

AC代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 500 + 5;
const int INF = 0x3f3f3f3f;
int N, M, S, D;
int value[maxn];

struct Edge
{
    int to, w, num, pre;
    Edge(int to, int w, int num, int pre) : to(to), w(w), num(num), pre(pre) {}
    bool operator > (const Edge &rhs) const
    {
        if(w != rhs.w)
            return w > rhs.w;
        return num < rhs.num;
    }
};
vector<Edge> edge[maxn];
int dis[maxn], ans[maxn], Next[maxn];
bool vis[maxn];
int nums[maxn];

void Dijkstra(int st)
{
    priority_queue<Edge, vector<Edge>, greater<Edge> > que;
    que.push(Edge(st, 0, value[st], -1));

    while(!que.empty())
    {
        Edge temp = que.top();
        que.pop();

        if(vis[temp.to])
            continue;

        vis[temp.to] = true;
        ans[temp.to] = temp.num;
        Next[temp.to] = temp.pre;

        for(int i = 0; i < edge[temp.to].size(); i++)
        {
            Edge node = edge[temp.to][i];

            if(dis[node.to] > temp.w + node.w)
            {
                dis[node.to] = temp.w + node.w;
                nums[node.to] = nums[temp.to];
                que.push(Edge(node.to, dis[node.to], value[node.to] + temp.num, temp.to));
            }
            else if(dis[node.to] == temp.w + node.w)
            {
                dis[node.to] = temp.w + node.w;
                nums[node.to] += nums[temp.to];
                que.push(Edge(node.to, dis[node.to], value[node.to] + temp.num, temp.to));
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    memset(dis, INF, sizeof(dis));
    memset(ans, INF, sizeof(ans));
    memset(Next, -1, sizeof(Next));
    memset(vis, false, sizeof(vis));
    cin >> N >> M >> S >> D;
    for(int i = 0; i < N; nums[i] = 1, i++)
        cin >> value[i];
    for(int i = 0; i < M; i++)
    {
        int x, y, w;
        cin >> x >> y >> w;
        edge[x].push_back(Edge(y, w, value[y], -1));
        edge[y].push_back(Edge(x, w, value[x], -1));
    }

    Dijkstra(S);
    memset(vis, false, sizeof(vis));

    cout << nums[D] << " "  << ans[D] << endl;

    stack<int> G;
    while(D != -1)
    {
        G.push(D);
        D = Next[D];
    }

    cout << G.top();
    G.pop();
    while(!G.empty())
    {
        cout << " " << G.top();
        G.pop();
    }
    cout << endl;
    return 0;
}

注明:这里因为输入保证救援可行且最优解唯一,因此记录路径条数的数组我们都标记成了1.

7-2 天梯地图 (30 分)

本题要求你实现一个天梯赛专属在线地图,队员输入自己学校所在地和赛场地点后,该地图应该推荐两条路线:一条是最快到达路线;一条是最短距离的路线。题目保证对任意的查询请求,地图上都至少存在一条可达路线。

输入格式:

输入在第一行给出两个正整数N(2 ≤ N ≤ 500)和M,分别为地图中所有标记地点的个数和连接地点的道路条数。随后M行,每行按如下格式给出一条道路的信息:

V1 V2 one-way length time

其中V1V2是道路的两个端点的编号(从0到N-1);如果该道路是从V1V2的单行线,则one-way为1,否则为0;length是道路的长度;time是通过该路所需要的时间。最后给出一对起点和终点的编号。

输出格式:

首先按下列格式输出最快到达的时间T和用节点编号表示的路线:

Time = T: 起点 => 节点1 => ... => 终点

然后在下一行按下列格式输出最短距离D和用节点编号表示的路线:

Distance = D: 起点 => 节点1 => ... => 终点

如果最快到达路线不唯一,则输出几条最快路线中最短的那条,题目保证这条路线是唯一的。而如果最短距离的路线不唯一,则输出途径节点数最少的那条,题目保证这条路线是唯一的。

如果这两条路线是完全一样的,则按下列格式输出:

Time = T; Distance = D: 起点 => 节点1 => ... => 终点

输入样例1:

10 15
0 1 0 1 1
8 0 0 1 1
4 8 1 1 1
5 4 0 2 3
5 9 1 1 4
0 6 0 1 1
7 3 1 1 2
8 3 1 1 2
2 5 0 2 2
2 1 1 1 1
1 5 0 1 3
1 4 0 1 1
9 7 1 1 3
3 1 0 2 5
6 3 1 2 1
5 3

输出样例1:

Time = 6: 5 => 4 => 8 => 3
Distance = 3: 5 => 1 => 3

输入样例2:

7 9
0 4 1 1 1
1 6 1 3 1
2 6 1 1 1
2 5 1 2 2
3 0 0 1 1
3 1 1 3 1
3 2 1 2 1
4 5 0 2 2
6 5 1 2 1
3 5

输出样例2:

Time = 3; Distance = 4: 3 => 2 => 5

AC代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 500 + 5;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to, length, times;
    Edge(int to, int length, int times) : to(to), length(length), times(times)
    {
        //cout << "time = " << times << endl;
    }
};
vector<Edge> G[maxn];
stack<int> ans_time[maxn];
stack<int> ans_dis[maxn];
int Dis[maxn], Time[maxn];
int n, m, S, D;
bool vis[maxn];

struct node_dis
{
    int to, length;
    stack<int> ans;
    node_dis(int to, int length) : to(to), length(length) {}
    bool operator > (const node_dis &rhs) const
    {
        if(length != rhs.length)
            return length > rhs.length;
        return ans.size() > rhs.ans.size();
    }
};

struct node_time
{
    int to, length, times;
    stack<int> ans;
    node_time(int to, int length, int times) : to(to), length(length), times(times) {}
    bool operator > (const node_time & rhs) const
    {
        if(times != rhs.times)
            return times > rhs.times;
        return length > rhs.length;
    }
};

void Dijkstra_dis()
{
    priority_queue<node_dis, vector<node_dis>, greater<node_dis> > que;
    que.push(node_dis(S, 0));

    while(!que.empty())
    {
        node_dis node = que.top();
        que.pop();

        if(vis[node.to])
            continue;

        vis[node.to] = true;
        ans_dis[node.to] = node.ans;

        for(int i = 0; i < G[node.to].size(); i++)
        {
            Edge edge = G[node.to][i];

            if(vis[edge.to])
                continue;

            if(Dis[edge.to] >= edge.length + node.length)
            {
                Dis[edge.to] = edge.length + node.length;
                node_dis m = node_dis(edge.to, Dis[edge.to]);
                m.ans = node.ans;
                m.ans.push(node.to);
                que.push(m);
            }
        }
    }
}

void Dijkstra_time()
{
    priority_queue<node_time, vector<node_time>, greater<node_time> > que;
    que.push(node_time(S, 0, 0));

    while(!que.empty())
    {
        node_time node = que.top();
        que.pop();

        if(vis[node.to])
            continue;

        vis[node.to] = true;

        ans_time[node.to] = node.ans;

        for(int i = 0; i < G[node.to].size(); i++)
        {
            Edge edge = G[node.to][i];
            //cout << edge.times << endl;
            if(vis[edge.to])
                continue;

            if(Time[edge.to] >= edge.times + node.times)
            {
                //cout << "time1 = " << edge.times << " time2 = " << node.times << endl;
                Time[edge.to] = edge.times + node.times;
                //cout << "T = " << Time[edge.to] << endl;
                node_time m = node_time(edge.to, node.length + edge.length, Time[edge.to]);
                m.ans = node.ans;
                m.ans.push(node.to);
                que.push(m);
            }
        }
    }
}

void printf(stack<int> &ans)
{
    stack<int> temp;
    while(!ans.empty())
    {
        temp.push(ans.top());
        ans.pop();
    }
    printf("%d", temp.top());
    temp.pop();
    while(!temp.empty())
    {
        printf(" => %d", temp.top());
        temp.pop();
    }
    printf(" => %d\n", D);
}

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 0; i < m; i++)
    {
        int v1, v2, one_way, length, times;
        scanf("%d%d%d%d%d", &v1, &v2, &one_way, &length, &times);
        if(one_way == 1)
            G[v1].push_back(Edge(v2, length, times));
        else if(one_way == 0)
        {
            G[v1].push_back(Edge(v2, length, times));
            G[v2].push_back(Edge(v1, length, times));
        }
    }
    scanf("%d%d", &S, &D);
    //cin >> S >> D;
    memset(Dis, INF, sizeof(Dis));
    memset(Time, INF, sizeof(Time));
    memset(vis, false, sizeof(vis));
    Dijkstra_dis();
    memset(vis, false, sizeof(vis));
    Dijkstra_time();
    //dfs(S, D, 0, 0, ans);

    if(ans_dis[D] == ans_time[D])
    {
        printf("Time = %d; ", Time[D]);
        printf("Distance = %d: ", Dis[D]);
        printf(ans_dis[D]);
    }
    else
    {
        printf("Time = %d: ", Time[D]);
        printf(ans_time[D]);
        printf("Distance = %d: ", Dis[D]);
        printf(ans_dis[D]);
    }
    return 0;
}

解析:就是两组dijkstra,我更倾向于使用优先队列优化,这样不仅容易理解并且时间复杂度也低。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值