单源最短路 -- 酋长的聘礼

单源最短路的实现拥有已知的下列方法:

1.SPFA算法

2.Dijkstra算法(朴素/堆优化)

3.Floyd算法

接下来简单讲解一下这些算法的应用差异:

SPFA算法:几乎能够用于所有的情形,除非题目不允许。

Dijkstra算法:朴素版用于点少边多,而堆优化之后点多的情形也能够使用,缺点就是无法处理边权为负的情形。

Floyd算法:基于DP的思路,但时间复杂度为O(n^3),需要根据题目设定的数据范围来做。

多数题目中都能够使用,重点是理解其写法,下面通过一道例题来进行解析:

Acwing 昂贵的聘礼​​​​​

题目解析:

这道题运用最短路的方法来做比较好理解并且方便想到,读取题目中的信息,能够得到:

1.物品的价值

2.替换的物品

3.物品的等级

接下来能够对这三个元素做文章,按照一般的图论题目进行画图分析,将每个物品设置成为一个节点,需要的价格设为边的权值,如下图所示:

我们已知,能够用其他更便宜的物品来代替原本自身价格过高的物品,因此我们从除酋长女儿外的其他节点起做最短路,最后求得所有的最少价格中再找出最小花费便能得到答案,这里用一个小技巧:

设置一个虚拟头节点作为起点,将酋长女儿作为终点,将除酋长女儿的其它所有点自身价格当作虚拟头节点到它们的边权值

这样只需要求一次最短路就行了。

但是这样做了话还需要思索一个问题:等级的划分

关于等级问题,我们在传入最短路计算函数的参数上做手脚,传入的是一个等级区间。

题目给我们的是交换物品的双方所需要在的一个等级差范围(假设为m),因此每次传入等级差为m的区间就行了,用于在最短路的条件判断中加入不允许两个点的等级差大于m。

下面就是代码展示,有2种不同的函数写法:

1.spfa算法

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 10010, INF = 0x3f3f3f3f;
int n, m;
int e[N], ne[N], h[N], w[N], idx;
int level[N];
int dist[N];
bool st[N];
void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int Dijkstra(int down, int up) {
    queue<int> q;
    q.push(0);
    memset(dist, 0x3f, sizeof dist);
    dist[0] = 0;
    memset(st, false, sizeof st);
    st[0] = true;
    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        st[t] = false;
        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            st[t] = false;
            if (dist[j] > dist[t] + w[i]) {
                dist[j] = dist[t] + w[i];
                if (!st[j] && level[j] >= down && level[j] <= up) {
                    st[j] = true;
                    q.push(j);
                }
            }
        }
    }
    return dist[1];
}
int main() {
    cin >> m >> n;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i++) {
        int cost, num;
        cin >> cost >> level[i] >> num;
        add(0, i, cost);
        for (int j = 0; j < num; j++) {
            int replace_id, replace_cost;
            cin >> replace_id >> replace_cost;
            add(replace_id, i, replace_cost);
        }
    }
    int res = INF;
    for (int i = level[1] - m; i <= level[1]; i++) res = min(res, Dijkstra(i, i + m));
    cout << res << endl;
    return 0;
}

```

2.Dijkstra算法

(1).朴素

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1010, INF = 0x3f3f3f3f;
int n, m;
int w[N][N], level[N];
int dist[N];
bool st[N];
int Dijkstra(int l, int r) {
    memset(dist, 0x3f, sizeof dist);
    dist[0] = 0;
    memset(st, false, sizeof st);
    for (int i = 0; i < n + 1; i++) {
        int t = -1;
        for (int j = 0; j <= n; j++) {
            if (!st[j] && (t == -1 || dist[j] < dist[t])) t = j;
        }
        st[t] = true;
        for (int j = 1; j <= n; j++) {
            if (level[j] >= l && level[j] <= r) {
                dist[j] = min(dist[t] + w[t][j], dist[j]);
            }
        }
    }
    return dist[1];
}
int main() {
    cin >> m >> n;
    memset(w, 0x3f, sizeof w);
    for (int i = 1; i <= n; i++) w[i][i] = 0;
    for (int i = 1; i <= n; i++) {
        int cost, num;
        cin >> cost >> level[i] >> num;
        w[0][i] = min(w[0][i], cost);
        for (int j = 0; j < num; j++) {
            int replace_id, replace_cost;
            cin >> replace_id >> replace_cost;
            w[replace_id][i] = min(w[replace_id][i], replace_cost);
        }
    }
    int res = INF;
    for (int i = level[1] - m; i <= level[1]; i++) res = min(res, Dijkstra(i, i + m));
    cout << res << endl;
    return 0;
}

(2).堆优化

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 10010, INF = 0x3f3f3f3f;
int n, m;
int e[N], ne[N], h[N], w[N], idx;
int level[N];
int dist[N];
bool st[N];
void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int Dijkstra(int down, int up) {
    priority_queue<PII, vector<PII>, greater<PII>> q;
    q.push({0, 0});
    memset(dist, 0x3f, sizeof dist);
    dist[0] = 0;
    memset(st, false, sizeof st);
    while (!q.empty()) {
        auto t = q.top();
        q.pop();
        int u = t.y;
        if (st[u]) continue;
        st[u] = true;
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if (dist[j] > dist[u] + w[i] && level[j] >= down && level[j] <= up) {
                dist[j] = dist[u] + w[i];
                q.push({dist[j], j});
            }
        }
    }
    return dist[1];
}
int main() {
    cin >> m >> n;
    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i++) {
        int cost, num;
        cin >> cost >> level[i] >> num;
        add(0, i, cost);
        for (int j = 0; j < num; j++) {
            int replace_id, replace_cost;
            cin >> replace_id >> replace_cost;
            add(replace_id, i, replace_cost);
        }
    }
    int res = INF;
    for (int i = level[1] - m; i <= level[1]; i++) res = min(res, Dijkstra(i, i + m));
    cout << res << endl;
    return 0;
}

总结:绝大多数的单源最短路问题都能够通过这三种算法解决,不过重要的是先理解题意再做,不要盲目就套模板,祝各位学算法的兄弟们一路长红^ ^。

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值