AcWing341. 洛谷P1073, NOIP2009 最优贸易
题目大意
\(~~~~~~\)一个投机倒把的奸商想要通过城市不太健全的贸易系统坑点钱,任意城市都可以买入或者卖出水晶球,他想尽量在便宜的城市买入,在贵的城市卖出,以此赚取更高的差价,他必须从一号城市开始旅行,到\(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 最优贸易的更多相关文章
- 【题解】洛谷P1073 [NOIP2009TG] 最优贸易(SPFA+分层图)
次元传送门:洛谷P1073 思路 一开始看题目嗅出了强连通分量的气息 但是嫌长没打 听机房做过的dalao说可以用分层图 从来没用过 就参考题解了解一下 因为每个城市可以走好几次 所以说我们可以在图上 ...
- 洛谷1073 NOIP2009 最优贸易
题目大意 C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双 ...
- 【洛谷P1073】最优贸易
题目大意:给定一个 N 个点,M 条边(存在反向边)的有向图,点有点权,求一条从 1 到 N 的路径上,任意选出两个点 p,q (p 在前,q在后),两点点权的差值最大. 根据最短路的 dp 思想,可 ...
- 【洛谷 P1073】 最优贸易 (Tarjan缩点+拓扑排序)
题目链接 先\(Tarjan\)缩点,记录每个环内的最大值和最小值. 然后跑拓扑排序,\(Min[u]\)表示到\(u\)的最小值,\(ans[u]\)表示到\(u\)的答案,\(Min\)和\(an ...
- [Luogu 1073] NOIP2009 最优贸易
[Luogu 1073] NOIP2009 最优贸易 分层图,跑最长路. 真不是我恋旧,是我写的 Dijkstra 求不出正确的最长路,我才铤而走险写 SPFA 的- #include <alg ...
- [NOIP2009]最优贸易(图论)
[NOIP2009]最优贸易 题目描述 CC 国有 \(n\) 个大城市和 \(m\) 条道路,每条道路连接这 \(n\) 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这 \(m\ ...
- 洛谷 P1073 最优贸易 & [NOIP2009提高组](反向最短路)
传送门 解题思路 很长的题,实际上在一个有向图(点有点权)中求一个从起点1到终点n的路径,使得这条路径上点权最大的点与点权最小的点的差值最大(要求必须从点权较小的点能够走到点权较大的点). ——最短路 ...
- 【洛谷P1073】[NOIP2009]最优贸易
最优贸易 题目链接 看题解后感觉分层图好像非常NB巧妙 建三层n个点的图,每层图对应的边相连,权值为0 即从一个城市到另一个城市,不进行交易的收益为0 第一层的点连向第二层对应的点的边权为-w[i], ...
- 洛谷 P1073 最优贸易 解题报告
P1073 最优贸易 题目描述 \(C\)国有\(n\)个大城市和\(m\)条道路,每条道路连接这\(n\)个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这\(m\)条道路中有一部分 ...
- 洛谷P1073 最优贸易 [图论,DP]
题目传送门 最优贸易 题目描述 C 国有n 个大城市和m 条道路,每条道路连接这n 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这m 条道路中有一部分为单向通行的道路,一部分为双向 ...
随机推荐
- 移动端300ms延迟问题和点击穿透问题
一.移动端300ms延迟问题: 一般情况下,如果没有经过特殊处理,移动端浏览器在派发点击事件的时候,通常会出现300ms左右的延迟.也就是说,当我们点击页面的时候移动端浏览器并不是立即作出反应,而是会 ...
- C语言中的位域的使用
转载:http://blog.sina.com.cn/s/blog_648d306d0100mv1c.html C语言中的位域的使用一.位域 有些信息在存储时,并不需要占用一个完整的字节, 而只需占几 ...
- Oracle 同义词详解(synonym)
Oracle 同义词详解(synonym) 一.Oracle同义词概念 Oracle 数据库中提供了同义词管理的功能.同义词是数据库方案对象的一个别名,经常用于简化对象访问和提高对象访问的安全性.在使 ...
- 个人数据保全计划:(1) NAS开箱
前言 从几年前第一个硬盘故障导致参赛的文件丢失之后,我就开始意识到数据安全的重要性,开始用各种云盘做备份,当时还不是百度云一家独大,我们也都没意识到网盘备份是极其不靠谱的行为,直到因为某些不可抗力因素 ...
- JDK 8之前日期和时间的API
JDK 8之前日期和时间的API(1) System类中的currentTimeMillis():返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差.称为时间戳. java.util ...
- 4.pygame快速入门-事件监听
事件event:游戏启动后,用户针对游戏的所有操作 监听:在游戏循环中,判断用户的具体操作 pygame中通过pygame.event.get()可以获得当前用户所做动作的事件列表 事件监听 wh ...
- 5.httprunner-hook机制
hook简介 httprunner3是基于python的pytest框架,里面也有前置和后置的概念 setup_hooks:开始执行前触发hook函数,主要用于请求预处理(签名,加密等) teardo ...
- JS逆向实战8——某网实战(基于golang-colly)
其实本章算不上逆向教程 只是介绍golang的colly框架而已 列表页分析 根据关键字搜索 通过抓包分析可知 下一页所请求的参数如下 上图标红的代表所需参数 所以其实我们真正需要的也就是Search ...
- 以开发之名 | bilibili会员购让IP在眼前动起来
随着ACG文化(二次元文化)影响力的不断提升,哔哩哔哩平台上衍生品消费群体不断扩大,手办行业迅速崛起.2017年,B站推出ACG衍生品消费品牌bilibili会员购,涵盖二次元手办销售等多项业务,拓展 ...
- ISCTF2022WP
ISCTF2022改名叫套CTF吧(bushi),博主菜鸡一个,套题太多,挑一些题写下wp,勿喷. MISC 可爱的emoji 下载下来是个加密压缩包,根据hint掩码爆破密码 得到密码:KEYI ...