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. [CG从零开始] 4. pyopengl 绘制一个正方形

    在做了 1-3 的基础工作后,我们的开发环境基本 OK 了,我们可以开始尝试利用 pyopengl 来进行绘制了. 本文主要有三个部分 利用 glfw 封装窗口类,并打开窗口: 封装 shader 类 ...

  2. Hive之命令

    Hive之命令 说明:此博客只记录了一些常见的hql,create/select/insert/update/delete这些基础操作是没有记录的. 一.时间级 select day -- 时间 ,d ...

  3. 图解不同版本的HTTP协议

    前言 大家好,我是蜗牛,今天我们聊聊HTTP协议,通过这篇文章我们能了解到不同版本HTTP优缺点.他们之间的性能差异以及现在主流的HTTP协议用的那个版本 HTTP/1.1 时代 HTTP/1.1 对 ...

  4. Vue3.x+element-plus+ts踩坑笔记

    闲聊 前段时间小颖在B站找了个学习vue3+TS的视频,自己尝试着搭建了一些基础代码,在实现功能的过程中遇到了一些问题,为了防止自己遗忘,写个随笔记录一下嘻嘻 项目代码 git地址:vue3.x-ts ...

  5. 在vue中的form表单中下拉框中的数据来自数据库查询到的数据

    文章目录 1.实现的效果: 2.前端html代码 3.js中的代码 4.后端的方法 1.实现的效果: 增加一个新的类型到数据库 2.前端html代码 需要注意的部分:prop后边是表单中的字段 v-m ...

  6. 4.pytest结合allure-pytest插件生成allure测试报告

    之前我们使用的测试报告插件是pytest-html 这次使用的插件是allure-pytest,更加美观强大 安装插件 pip3 install allure-pytest 安装allure(Mac) ...

  7. VS使用正则表达式删除程序中的空行

    Ctrl+H; 需要替换的正则表达式 ^(?([^\r\n])\s)*\r?$\r?\n

  8. 十、Pod的init containers

    Pod 的 init Containers Pod 我们可以分为两类,一种属于自主式 Pod ,还有一种属于控制器管理的 Pod . 一.Pod 的 initContainers 基本概念: ​Pod ...

  9. Nginx 使用自签名证书实现 https 反代 Spring Boot 中碰到的页面跳转问题

    问题一:页面自动跳转到 80 端口 问题描述 最近在使用Nginx反代一个Spring Boot项目中碰到了一个问题,使用 Spring Boot 中的 redirect: 进行页面跳转的时候,通过 ...

  10. Java安全之Resin2内存马

    Java安全之Resin2内存马 环境 resin2.1.17 添加Filter分析 依然是web.xml注册一个filter,debug进去看注册流程 debug dofilter逻辑时看到如下代码 ...