【图论】B007_最优贸易路线(2 × dijkstra / 分图层)

一、题目描述

C国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。

任意两个城市之间最多只有一条道路直接相连。

这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为1条。

C国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。

但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。

商人阿龙来到C国旅游。

当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚一点旅费。

设C国 n 个城市的标号从 1~n,阿龙决定从1号城市出发,并最终在 n 号城市结束自己的旅行。

在旅游的过程中,任何城市可以被重复经过多次,但不要求经过所有 n 个城市。

阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。

因为阿龙主要是来C国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。

现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。

请你告诉阿龙,他最多能赚取多少旅费。

注意:本题数据有加强。

输入格式

  • 第一行包含 2 个正整数 n 和 m,中间用一个空格隔开,分别表示城市的数目和道路的数目。
  • 第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。
  • 接下来 m 行,每行有 3 个正整数,x,y,z,每两个整数之间用一个空格隔开。
  • 如果z=1,表示这条道路是城市 x 到城市 y 之间的单向道路;如果z=2,表示这条道路为城市 x 和城市 y 之间的双向道路。

输出格式

  • 一个整数,表示答案。

数据范围

  • 1≤n≤100000,
    1≤m≤500000,
    1≤各城市水晶球价格≤100
输入样例:
5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2
输出样例:
5

二、题解

题目大意:小龙到 C 国观光的同时顺便通过买卖当地珠宝赚取一些钱来减轻路由开销,C 国中有些城市之间是双向连接,有些是单向的,问小龙最多能赚多少。
在这里插入图片描述
假设 1~n 号城市的水晶球价格分别为 4,3,5,6,1。 阿龙可以选择如下一条线路:1->2->3->5,并在 2 号城市以 3 的价格买入水晶球,在 3 号城市以 5 的价格卖出水晶球,赚取的旅费数为 2。 阿龙也可以选择如下一条线路 1->4->5->4->5,并在第 1 次到达 5 号城市时以 1 的价格 买入水晶球,在第 2 次到达 4 号城市时以 6 的价格卖出水晶球,赚取的旅费数为 5。

方法一:2 × Dijkstra

思路

假如我是小龙,那么我很容易想到在最便宜的地方买,在价格最高的地方卖,但给定的图可能存在这种问题:就是说,我在某个地方低价买入之后,进入了一个价格相对较高但不是最高的城市,但是我进去之后回不了原来的城市了,但是原来的城市的下下个城市是最高的价格的,这样我就求不出最大收益了。

所以,跑一遍 Dijkstra 或 SPFA 解决不了全部样例。怎么做呢?

我的思路是:第一遍 Dijkstra 求出每个点到源点 1 的最小价钱,然后从第 n 个点为起点再跑一遍 Dijkstra,最后取最大值:dist2[1...V] - dist1[1...V]

可能存在的疑惑:

  • Q1:为什么要跑第二遍呢?上面说了起点 1 能到的最便宜的地方,未必能到最鬼的地方,所以第二遍的意义是保证从 1 ---> cheapPos ---> luxuriousPos ---> V 是连通的,这样求出来的利润才会是合法的。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static int V, E, S;
	static Edge[] edges1, edges2;
	static int[] dist1, dist2;
	static int[] head1, head2;
	static boolean[] vis;
	static int tot1, tot2;
	static int INF = 0x3f3f3f3f;
	static int[] p;
	static int maxv = 100010, maxe = 500000 + 50;
	
	private static void addEdge1(int u, int v) {
	    edges1[++tot1] = new Edge();
		edges1[tot1].to = v;
		edges1[tot1].next = head1[u];
		head1[u] = tot1;
	}
	
	private static void addEdge2(int u, int v) {
		edges2[++tot2] = new Edge();
		edges2[tot2].to = v;
		edges2[tot2].next = head2[u];
		head2[u] = tot2;
	}
	
	static void dijkstra1() {	//收购低价
		vis = new boolean[maxv];
		dist1 = new int[maxv];
		S = 1;
		Arrays.fill(dist1, INF);
		dist1[S] = p[S];				//买入
		Queue<Node> q = new PriorityQueue<>((e1, e2) -> e1.p - e2.p);
		q.add(new Node(S, p[S]));
		
		while (!q.isEmpty()) {
			Node t = q.poll();
			int v = t.id;
			for (int i = head1[v]; i != 0; i = edges1[i].next) {
			    int to = edges1[i].to;
				if (dist1[to] > dist1[v]) {
					dist1[to] = Math.min(dist1[v], p[to]);
					q.add(new Node(edges1[i].to, dist1[to]));
				}
			}
		}
	}
	static void dijkstra2() {	//收购低价
		vis = new boolean[maxv];
		dist2 = new int[maxv];
		Queue<Node> q = new PriorityQueue<>((e1, e2) -> e2.p - e1.p);
		S = V;
		Arrays.fill(dist2, -INF);
		dist2[S] = p[S];				//买入
		q.add(new Node(S, p[S]));
		
		while (!q.isEmpty()) {
			Node t = q.poll();
			int v = t.id;
			for (int i = head2[v]; i != 0; i = edges2[i].next) {
			    int to = edges2[i].to;
				if (dist2[to] < dist2[v]) {
					dist2[to] = Math.max(dist2[v], p[to]);
					q.add(new Node(edges2[i].to, dist2[to]));
				}
			}
		}
	}
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
		V = sc.nextInt();
		E = sc.nextInt();
		p = new int[V+1];
		for (int i = 1; i <= V; i++) {
			p[i] = sc.nextInt();
		}
		edges1 = new Edge[2*maxe];
		edges2 = new Edge[2*maxe];
    	head1 = new int[maxv];
		head2 = new int[maxv];
		for (int i = 0; i < E; i++) {
			int from = sc.nextInt();
			int to = sc.nextInt();
			int z = sc.nextInt();
			addEdge1(from, to);
			addEdge2(to, from);
			
			if (z == 2) {
				addEdge1(to, from);
				addEdge2(from, to);
			}
		}
		dijkstra1();
		dijkstra2();
		int max = -INF;
		for (int i = 1; i <= V; i++) {
			max = Math.max(max, dist2[i] - dist1[i]);
		}
		System.out.println(max);
	}
	static class Node {
		int id, p;
		Node (int id, int p) {
			this.id = id;
			this.p = p;
		}
	}
	static class Edge {
		int to, next;
		public Edge() {}
	}
}

复杂度分析

  • 时间复杂度: O ( E l o g V ) O(ElogV)
  • 空间复杂度: O ( . ) O(.)

方法二:分图层


复杂度分析

  • 时间复杂度: O ( ) O()
  • 空间复杂度: O ( ) O()
发布了691 篇原创文章 · 获赞 151 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/105511209