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. ofd格式文件转换成pdf格式的方法

    ofd格式文件很多人还比较陌生,很多人接收到文件都不知如何打开阅读,把文件发给对方,还需要对方安装个专门的阅读软件,我们还有另一个办法,就是将OFD文件转换为PDF格式文件,然后把PDF格式文件发给对 ...

  2. JSP实现登录功能(页面带样式)

    功能要求 1.完成两个页面 2.第一个登陆页面login. jsp 3.第二个用户管理页面useManage. jsp 4.有登录功能(能进行用户名密码的校验,用户名若为自己的学号密码为班级号,允许登 ...

  3. day07-2MySQL索引

    MySQL索引 说起提高数据库性能,索引是最物美价廉的东西了.不用加内存,不用改程序,不用调sql,查询速度就能提高千百倍. 例子 首先,创建一个有800万条数据的表 -- 创建测试数据库 tmp C ...

  4. 『现学现忘』Git基础 — 36、标签tag(一)

    目录 1.标签介绍 2.列出标签 3.创建标签 (1)标签的分类 (2)附注标签 (3)轻量标签 4.后期打标签 1.标签介绍 软件的某个发行版本所对应的,其实就是软件开发过程中,某一个阶段的最后一次 ...

  5. Hudi 数据湖的插入,更新,查询,分析操作示例

    Hudi 数据湖的插入,更新,查询,分析操作示例 作者:Grey 原文地址: 博客园:Hudi 数据湖的插入,更新,查询,分析操作示例 CSDN:Hudi 数据湖的插入,更新,查询,分析操作示例 前置 ...

  6. 后端框架学习1-----Spring

    Spring学习笔记 spring全家桶:https://www.springcloud.cc/spring-reference.html spring中文文档:http://c.biancheng. ...

  7. 编写一个jsp页面,利用Scriptlet编写一段计算代码,要求用零作为除数,并使用page指令将错误信息显示在另外一个jsp页面,产生的错误信息为“错误,不能用0做除数”

    文章目录 1.测试结果: 2.结果计算页面 3.错误处理页面 1.测试结果: 2.结果计算页面 <%@ page language="java" contentType=&q ...

  8. logback在springBoot项目中的使用 springboot中使用日志进行持久化保存日志信息

    文章目录 1.xml文件的编写 2.实现的效果 2.1 日志保存到磁盘 2.2 控制台输出的效果 放置的位置 1.xml文件的编写 logback-spring.xml <?xml versio ...

  9. 使用python获取window注册表值的方法

    提供regfullpath的方法,可以自行封装个regpath的函数import loggingimport pywintypes import win32apiimport win32con def ...

  10. 齐博x1标签之异步加载标签数据

    为什么要异步加载标签?他有什么好处 如果一个页面的标签太多,又或者是页面中某一个标签调用数据太慢的话,就会拖慢整个页面的打开,非常影响用户体验.这个时候,用异步加载的话,就可以一块一块的显示,用户体验 ...