POJ - 1860:Currency Exchange & 最短路变形-最小值最大

题目描述:

N个点,M条边,每条边有权值。求一条1号点到N号点的路径,要求使得路径中的边权最小值最大。                           

Input:

多组输入,第一行给一个T
每一组第一行给两个数n和m。(1 <= n <= 1000)
接下来m行,每行三个数u,v,w代表路径的两个端点与边权 

(1 <= u,v <= n , 0< w <= 1e6)
保证两点间只有一条边,该图为无向图。                                       Output:

第i组数据先输出 "Scenario #i:"
然后输出该路径上的最小边权,输出一个空行结束。
保证有解

样例:

sample input: sample output
Scenario #1:
3  3 1
1 2 3
1 3 4
2 3 5

思路: 

乍一看这道题非常的简单,就是求一个最短路用dijkstra就能解决,但是细看一下,“最小值最大”让我蒙住了,脑子里第一跳出来的是《数学里的最值问题》,但是上完课后感觉事实并非如此,这道题的最小值最大是和dijkstra相挂钩甚至可以说是非常相似,但是原理却很难理解,待我们细细深究。

普通dijkstra&最小值最大

普通的dijkstra无论是没有进行优化单纯使用松弛操作的代码还是使用优先队列进行优化后的码,其最重要的操作无论题怎样出,怎样和别的算法进行嵌套,这“if(dis[v]>dis[u]+w) dis[v]=dis[u]+w;”都是万变不离其宗的。

关于最小值最大,其原理虽较难于理解,但是代码却仅仅改动了dijkstra核心代码的一小部分。将上面的代码改成了“if(dis[v]>max(dis[u],w) dis[v]=dis[u]+w;” 对于理解困难者将其背过即可                                                                                                                                           

最小值最大的原理:
"
当我们在解决带权有向图中的单源最短路径问题时,需要找到从起点s到所有其他节点的最短路径。在这个问题中,路径的长度是指路径上所有边的权值之和。而“路径中的边权最小值最大”则是指,对于所有从起点s到其他节点的路径,选择其中边权最小值最大的路径作为最短路径。也就是说,最短路径的长度是所有路径中边权最小值最大的路径的边权最小值。 
举个例子,假设我们有一个带权有向图,其中起点为s,终点为t,图中有三条路径s->t,它们的边权分别为2、3和4。根据“路径中的边权最小值最大”的定义,我们需要选择边权最小值最大的路径作为最短路径。在这个例子中,边权最小值最大的路径是第三条路径,因为它的边权最小值为4,而其他两条路径的边权最小值分别为2和3。因此,最短路径的长度为4。 需要注意的是,如果有多条路径的边权最小值相同,那么我们可以任选其中一条作为最短路径。同时,如果没有从起点s到终点t的路径,那么最短路径的长度为无穷大。
"

这段话很详细的讲出了dijkstra和其变形的最根本的区别,"最小值最大"就是指我从一个点走出的多条路径我要确保其走的每个点的权值都尽量的小(至少当前路径的走法没法找出第二种走法的权值总和比其更小),在走完的多条路径中,我再选取权值最大的一条路径进行输出,而"if(dis[v]>max(dis[u],w) dis[v]=dis[u]+w;"这句话能确保我们从队列中弹出来的每个点的权值在当前走法中是最小的一个,使每条路径走完都能达到"最小值"这个特性。在走完队列以后,我们在main函数中再用min将每条"最小值"的路都筛一遍,筛选出满足条件的唯一一条路径。


本题思路: 

因为题目中说道确保题目有唯一解,是这道题能简化特判的难度,所以就按照我们上文介绍的"最小值最大"的特性将dijkstra进行变形,注意好细节,《小心而谨慎》的码完就好。

附上代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <map>
#include <vector>

const int N = 1005;

struct EDGE {
    int v, w, next;
} e[N * N * 2];

int head[N], tot = 1;
int father[N];

void add(int u, int v, int w) {//普通dijkstra建边无需多说
    e[tot].v = v;
    e[tot].w = w;
    e[tot].next = head[u];
    head[u] = tot ++;
}

void init(int n) {//对father数组进行初始化,一开始都没有父节点,将其值赋为-1
    tot = 1;
    for (int i = 0; i <= n; i++) {
        head[i] = 0;//初始化
        father[i] = -1;
    }
}

struct Node {
    int pos, dis;

    Node() {};

    Node(int pos, int dis): pos(pos), dis(dis) {};

    bool operator < (const Node &r) const {
        return dis > r.dis;
    }
};

int dis[N], vis[N];

void dijkstra(int s) {
    std::priority_queue<Node> q;
    memset(dis, 0x3f, sizeof dis);
    memset(vis, 0, sizeof vis);
    dis[s] = 0;
    q.push(Node(s, 0));
    while (!q.empty()) {
        Node f = q.top();
        q.pop();
        int u = f.pos;
        if (vis[u]) continue;
        vis[u] = 1;
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].v;
            int w = e[i].w;
            if (!vis[v] && dis[v] > std::max(dis[u], w)) {
                dis[v] = std::max(dis[u], w);
                q.push(Node(v, dis[v]));
                father[v] = u;
            }
        }
    }
}

void solve() {
    int n, m;
    std::cin >> n >> m;
    init(n);//进行初始化
    for (int i = 0; i < m; i++) {
        int u, v, w;
        std::cin >> u >> v >> w;
        add(u, v, w);
    }
    dijkstra(1);
    int p = n;
    int ans = 0x3f3f3f3f;
    while (p != 1) {//对应init函数father数组赋-1的行为,说明p=-1到了终点需退出
        ans = std::min(ans, dis[p]);
        p = father[p];
    }
    std::cout << ans << '\n';
}

int main() {
    // freopen("/Users/chant/in.txt", "r", stdin);
    int T;
    std::cin >> T;
    for (int i = 1; i <= T; i++) {
        printf("Scenario #%d:\n", i);
        solve();
    }
    return 0;
}

 总结:本题就是最小值最大及dijkstra的变形的模板题,重点在于理解最小值最大的意义,剩下的就非常简单了。

创作不易,请勿白嫖!麻烦给个三连+关注!谢! 

猜你喜欢

转载自blog.csdn.net/2301_76331300/article/details/131652062
今日推荐