AcWing题目传送门

洛谷题目传送门

题目大意

\(~~~~~~\)一个投机倒把的奸商想要通过城市不太健全的贸易系统坑点钱,任意城市都可以买入或者卖出水晶球,他想尽量在便宜的城市买入,在贵的城市卖出,以此赚取更高的差价,他必须从一号城市开始旅行,到\(n\)号城市结束。请问他最多可以赚多少钱?

解题思路

\(~~~~~~\)这题乍一看貌似是毫无头绪的,但是我们可以通过差价高推出,他应该价格尽量低 买入,价格尽量高的卖出,所以我们可以在从\(1\sim n\) 的路径中选择一个分界点,记作\(k\),这个\(k\)点的含义就是奸商决定在\(1\sim k\)的城市中买入,\(k\sim n\)中的城市卖出,基于我们的买入尽量低,卖出尽量高原则,我们可以得到我们的基本思路:

\(~~~~~~\)枚举所有的\(k\)点,找出\(1\sim k\)中需要的价格最小的点(有路径可以到达的),用\(dmin\)数组记录, 再找出\(k\sim n\)中需要的价格最大的点(存在到\(n\)的路径的),用\(dmax\)数组记录,最后统计答案时找出\(dmax_{i}-dmin_{i}\) 的值最大的点

具体实现

\(~~~~~~\)怎么找出\(1\sim k\)中的最小点呢,难道是枚举每条\(a_{i}\sim k(\forall a_{i}\in [1,k])\)的路中最便宜的点吗?这样未免也太慢了。我们可以采用\(SPFA\)算法,枚举从\(1\)开始的单源最短路(这里最短路的松弛操作需要略做改动),这样对于\(\forall k\in [1,n]\),所有的最短距离都计算好了。

对于\(k\sim n\)的最大点我们也采用类似以上的做法:

\(~~~~~~\)我们枚举从\(k\)开始的最短路,松弛操作同样改动,那我们就可以算出\(\forall k\in [1,n]\)到\(n\)点的最大价值了。但是这样需要做\(n\)遍\(SPFA\),一定是不能再时间上通过的,所以该算法应该经过一点改进。

\(~~~~~~\)经过观察可以发现所有的汇点都是\(n\),我们就可以自然地建一个反图(将所有的边方向反转),这样子反图上跑出来的以\(n\)为源点的最长路,就是原图上各点到\(n\)的最长路了。

对于代码

\(~~~~~~~~~~~~~~~~~~~~~~~~\)我们可以写两个\(SPFA\),一个求最短路,一个求最长路

\(~~~~~~~~~~~~~~~~~~~~~~~~\)也可以合并处理,这里两份代码都放上来了

代码

两个\(SPFA\)

#include <queue>
#include <cstring>
#include <iostream> using namespace std; const int N = 3e5 + 10, M = 2e6 + 10, INF = 0x3f3f3f3f; int dmax[N], dmin[N], st[N];
int h[N], rh[N], e[M], ne[M], w[N], idx;
int n, m; void add(int *h, int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
} void spfa()
{
queue<int> q;
memset(st, 0, sizeof st);
memset(dmin, 0x3f, sizeof dmin); dmin[1] = w[1], st[1] = true, q.push(1); while (q.size())
{
auto t = q.front(); q.pop();
st[t] = false ; for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (dmin[j] > min(dmin[t], w[j]))
{
dmin[j] = min(dmin[t], w[j]);
if (!st[j]) st[j] = true, q.push(j);
}
}
}
} void rspfa()
{
queue<int> q;
memset(st, 0, sizeof st);
memset(dmax, 0xcf, sizeof dmax); dmax[n] = w[n], st[n] = true, q.push(n); while (q.size())
{
auto t = q.front(); q.pop();
st[t] = false ; for (int i = rh[t]; ~i; i = ne[i])
{
int j = e[i];
if (dmax[j] < max(dmax[i], w[j]))
{
dmax[j] = max(dmax[i], w[j]);
if (!st[j]) q.push(j);
}
}
}
} int main()
{
scanf("%d%d", &n, &m); for (int i = 1; i <= n; i ++ )
scanf("%d", &w[i]); memset(h, -1, sizeof h);
memset(rh, -1, sizeof rh); while (m -- )
{
int u, v, t;
scanf("%d%d%d", &u, &v, &t);
add(h, u, v), add(rh, v, u);
if (t == 2) add(h, v, u), add(rh, u, v);
} spfa(), rspfa(); int res = 0;
for (int i = 1; i <= n; i ++ )
res = max(res, dmax[i] - dmin[i]);
printf("%d\n", res); return 0;
}

观察到我们写的两个\(SPFA\)其实有很多相似点,所以可以合并成一个,加上一些参数就行(用来区分, 例如:遍历的邻接表不同,起点不同,要求的最短和最长性质不同)

但是在合并的\(SPFA\)中要注意:\(memset\)的时候因为我们传进去的距离数组只是一个指针,所以与我们要的字节大小不符合,所以应该写成memset(d, 0x3f, sizeof dmin);当然这只是示例

\(合并的SPFA\)

#include <queue>
#include <cstring>
#include <iostream> using namespace std; const int N = 2e5 + 10, M = 1e6 + 10, INF = 0x3f3f3f3f; int dmax[N], dmin[N], st[N];
int h[N], rh[N], e[M], ne[M], w[N], idx;
int n, m; void add(int *h, int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
} void spfa(int *h, int *d, int S, int type)
{
memset(st, 0, sizeof st);
if (type) memset(d, 0xcf, sizeof dmax);
else memset(d, 0x3f, sizeof dmin); queue<int> q;
d[S] = w[S], q.push(S), st[S] = true; while (q.size())
{
auto t = q.front(); q.pop();
st[t] = false ; for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (type) {
if (d[j] < max(d[t], w[j]))
{
d[j] = max(d[t], w[j]);
if (!st[j]) q.push(j);
}
}
else {
if (d[j] > min(d[t], w[j]))
{
d[j] = min(d[t], w[j]);
if (!st[j]) q.push(j);
}
}
}
}
} int main()
{
scanf("%d%d", &n, &m); for (int i = 1; i <= n; i ++ )
scanf("%d", &w[i]); memset(h, -1, sizeof h);
memset(rh, -1, sizeof rh); while (m -- )
{
int u, v, t;
scanf("%d%d%d", &u, &v, &t);
add(h, u, v), add(rh, v, u);
if (t == 2) add(h, v, u), add(rh, u, v);
} spfa(h, dmin, 1, 0); // 正着跑最短路
spfa(rh, dmax, n, 1); // 反着跑最长路 int res = 0;
for (int i = 1; i <= n; i ++ )
res = max(res, dmax[i] - dmin[i]);
printf("%d\n", res); return 0;
}

写法对比

两个\(SPFA\):时间\(250ms\),码量\(94\)行,容易\(Debug\)(原因:分开逻辑清晰一点)

一个\(SPFA\):时间\(227ms\),码量\(80\)行,容易出\(Bug\),不好\(Debug\)(原因:我菜)

\(\color{Green}{Accepted!}\)

AcWing341. 洛谷P1073, NOIP2009 最优贸易的更多相关文章

  1. 【题解】洛谷P1073 [NOIP2009TG] 最优贸易(SPFA+分层图)

    次元传送门:洛谷P1073 思路 一开始看题目嗅出了强连通分量的气息 但是嫌长没打 听机房做过的dalao说可以用分层图 从来没用过 就参考题解了解一下 因为每个城市可以走好几次 所以说我们可以在图上 ...

  2. 洛谷1073 NOIP2009 最优贸易

    题目大意 C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双 ...

  3. 【洛谷P1073】最优贸易

    题目大意:给定一个 N 个点,M 条边(存在反向边)的有向图,点有点权,求一条从 1 到 N 的路径上,任意选出两个点 p,q (p 在前,q在后),两点点权的差值最大. 根据最短路的 dp 思想,可 ...

  4. 【洛谷 P1073】 最优贸易 (Tarjan缩点+拓扑排序)

    题目链接 先\(Tarjan\)缩点,记录每个环内的最大值和最小值. 然后跑拓扑排序,\(Min[u]\)表示到\(u\)的最小值,\(ans[u]\)表示到\(u\)的答案,\(Min\)和\(an ...

  5. [Luogu 1073] NOIP2009 最优贸易

    [Luogu 1073] NOIP2009 最优贸易 分层图,跑最长路. 真不是我恋旧,是我写的 Dijkstra 求不出正确的最长路,我才铤而走险写 SPFA 的- #include <alg ...

  6. [NOIP2009]最优贸易(图论)

    [NOIP2009]最优贸易 题目描述 CC 国有 \(n\) 个大城市和 \(m\) 条道路,每条道路连接这 \(n\) 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这 \(m\ ...

  7. 洛谷 P1073 最优贸易 & [NOIP2009提高组](反向最短路)

    传送门 解题思路 很长的题,实际上在一个有向图(点有点权)中求一个从起点1到终点n的路径,使得这条路径上点权最大的点与点权最小的点的差值最大(要求必须从点权较小的点能够走到点权较大的点). ——最短路 ...

  8. 【洛谷P1073】[NOIP2009]最优贸易

    最优贸易 题目链接 看题解后感觉分层图好像非常NB巧妙 建三层n个点的图,每层图对应的边相连,权值为0 即从一个城市到另一个城市,不进行交易的收益为0 第一层的点连向第二层对应的点的边权为-w[i], ...

  9. 洛谷 P1073 最优贸易 解题报告

    P1073 最优贸易 题目描述 \(C\)国有\(n\)个大城市和\(m\)条道路,每条道路连接这\(n\)个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这\(m\)条道路中有一部分 ...

  10. 洛谷P1073 最优贸易 [图论,DP]

    题目传送门 最优贸易 题目描述 C 国有n 个大城市和m 条道路,每条道路连接这n 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这m 条道路中有一部分为单向通行的道路,一部分为双向 ...

随机推荐

  1. Linux+Wine运行QQTIM (2022年9月)

    测试的版本Tim3.4.0 QQ9.6.7 如果你的系统没有Wine先装Wine,Wine在各大发行版的源都能找到.记住32位和64位的Wine都要装 去https://tubentubentu.pa ...

  2. Azure Kubernetes(AKS)部署及查看应用资源

    简介 上一篇文章讲解了如何使用Azure DevOps持续部署应用到Azure Kubernetes上.但是部署是否成功?会不会遇到什么问题?项目运行中是否会出现问题?我们该怎么样查看这些问题,并且对 ...

  3. 齐博x1标签实例:调用多个圈子同时调用相关会员

    看这一篇之前,请先看上一篇,因为他们有关联性比如要实现这样的效果 可以通过下面的代码可以实现 {qb:tag name="xxx" type="qun" row ...

  4. React魔法堂:size-sensor源码略读

    前言 echarts-for-react在对echarts进行轻量级封装的基础上,额外提供图表尺寸自适应容器尺寸的这小而实用的功能,而这功能的背后就是本文想介绍的size-sensor了. 源码介绍 ...

  5. MRR和Hits@n

    使用 MRR/Hits@n 评估链路预测 平均倒数秩(Mean reciprocal rank,MRR) MRR是一种衡量搜索质量的方法.我们取一个未被破坏的节点,找到距离定义为相似性分数的" ...

  6. C语言整人关机程序

    #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char input ...

  7. 14.api根路由

    我们可以通过使用超链接来提高我们APi的内聚力和可发现性   一.为我们的API创建一个根路径 我们的视图有很多个url,但是没有一个入口点,可以使用@api_view创建一个根路径 #views.p ...

  8. 机器学习实战-AdaBoost

    1.概念 从若学习算法出发,反复学恶习得到一系列弱分类器(又称基本分类器),然后组合这些弱分类器构成一个强分类器.简单说就是假如有一堆数据data,不管是采用逻辑回归还是SVM算法对当前数据集通过分类 ...

  9. OpenMP 入门

    OpenMP 入门 简介 OpenMP 一个非常易用的共享内存的并行编程框架,它提供了一些非常简单易用的API,让编程人员从复杂的并发编程当中释放出来,专注于具体功能的实现.openmp 主要是通过编 ...

  10. Spring boot pom 配置文件

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...