当前位置: 首页 > news >正文

最短路spfa和多层图(P1073 [NOIP 2009 提高组] 最优贸易)题解

题目3

P1073 [NOIP 2009 提高组] 最优贸易 - 洛谷

目的时在一个地方以最低买入价格买入商品,再寻找另一个节点,以最高的售价卖出

但是这道题没有边权,只有点权。还有值得注意的一点是有些边不是双向边。

当我们输入每个点的点权时,不妨重新建一层图:

1→2层表示买入值,2→3表示卖出,从1-n点的权值最大

由于可以任意走动,所以我们可以建一张图,令图上的边全都是0,表示走动对我最终的结果没有影响。

买入操作:建立一条有向边转移到一张新图上,边的大小为-v[i],指向点i所能到达的点(在第二层图上)。

卖出操作:建立一条有向边转移到第三层图上,边的大小为v[i],指向i所能到达的点(在第三层图上)。

可以发现,从第一层图走到第二层图走到第三层图走到终点,这就是一个合法的选择。

来看建图的代码:

for(int i=1;i<=n;i++){int c=read();add(i,i+n,-c),add(i+n,i+2*n,c);
}
for(int i=1;i<=m;i++){int x,y,z;x=read();y=read();z=read();if(z==1){add(x,y);add(x+n,y+n);add(x+2*n,y+2*n);}else{add(x,y);add(x+n,y+n);add(x+2*n,y+2*n);add(y,x);add(y+n,x+n);add(y+2*n,x+2*n);}
}

读入点权的时候,连当前点到下一层图的同样编号的点一条边,边权为当前点的点权。

因为第一层到第二层代表的是在当前节点买入,所以这样的边权表示他"赚了"a元,当然他现在买入东西是亏钱了,所以可以看作赚了-a元。同样,第二层到第三层表示卖出去了东西,那么两点之间的边权表示他赚了b元。如此一来,讲这俩的权加上就表示他一共赚了多少。

为什么不能用dijkstra呢?

因为明显按照上述思路会有负权,也会有环。所以我们选择spfa

来看spfa部分

void spfa(){memset(d,-0x7f,sizeof(d));queue<int>q;q.push(1);book[1]=true;d[1]=0;while(!q.empty()){int x=q.front();q.pop();book[x]=false;for(int i=head[x];i;i=ne[i]){int y=to[i];if(d[y]<d[x]+v[i]){d[y]=d[x]+v[i];if(!book[y]){book[y]=true;q.push(y);}}}}
}

先对于处理权的数组附上极大值,然后将起点(1)加入队列,把1到1的权设置为0;然后在队列非空的前提下找更大的值。也就是找最长路。因为上面也讲了,我们需要计算从起点到终点的赚钱最多。支出的钱已经是负数,所以我们只需要求边权相加最大即可

值得注意的是,如果d[3*n],因为第三层表示卖出,所以d[3*n]表示最终结果。如果它是负数,就表示赚不到钱,输出0;

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e6+5;
int read(){int s=0,fl=1;char w=getchar();while(w>'9'||w<'0'){if(w=='-')fl=-1;w=getchar();}while(w<='9'&&w>='0'){s=s*10+(w^48);w=getchar();}return fl*s;
}
void out(int x){if(x<0)putchar('-'),x=-x;if(x<10)putchar(x+'0');else out(x/10),putchar(x%10+'0');
}
int n,m;
int v[N],head[N],ne[N],to[N],tot,d[N];
bool book[N];
void add(int x,int y,int t=0){to[++tot]=y;ne[tot]=head[x];head[x]=tot;v[tot]=t;
}
void spfa(){memset(d,-0x7f,sizeof(d));queue<int>q;q.push(1);book[1]=true;d[1]=0;while(!q.empty()){int x=q.front();q.pop();book[x]=false;for(int i=head[x];i;i=ne[i]){int y=to[i];if(d[y]<d[x]+v[i]){d[y]=d[x]+v[i];if(!book[y]){book[y]=true;q.push(y);}}}}
}
int main(){n=read();m=read();for(int i=1;i<=n;i++){int c=read();add(i,i+n,-c),add(i+n,i+2*n,c);}for(int i=1;i<=m;i++){int x,y,z;x=read();y=read();z=read();if(z==1){add(x,y);add(x+n,y+n);add(x+2*n,y+2*n);}else{add(x,y);add(x+n,y+n);add(x+2*n,y+2*n);add(y,x);add(y+n,x+n);add(y+2*n,x+2*n);}}spfa();out(d[3*n]<0?0:d[3*n]);return 0;
}

http://www.dtcms.com/a/337622.html

相关文章:

  • Linux系统等保三级安全加固执行手册(ReahtCentosKylin)
  • mq存量消息如何处理
  • STM32G4 Park及反Park变换(一)matlab建模
  • Spark 运行流程核心组件(三)任务执行
  • C语言基础:变量与进制详解
  • 直播美颜SDK架构揭秘:动态贴纸功能的实现原理与性能优化
  • 计算机网络技术-交换机配置(Day.2)
  • 戴尔易安信 PowerEdge R540服务器系统安装教程
  • 深度学习篇---卷积
  • 远程访问公司内网电脑怎么操作?3个简单通用的跨网异地连接管理计算机方法
  • IoT/透过oc_lwm2m和at源码,分析NB-IoT通信模组和主板MCU之间的通信过程
  • 自建K8s集群无缝集成阿里云RAM完整指南
  • 重温 K8s 基础概念知识系列五(存储、配置、安全和策略)
  • Kubernetes(K8s)常用命令全解析:从基础到进阶
  • kubeadm方式部署k8s集群
  • 备考国央企-算法笔记-01链表
  • HakcMyVM-Friendly
  • MongoDB Windows 系统实战手册:从配置到数据处理入门
  • Esp32基础(③旋转编码器)
  • 用一个label控件随便显示一些字(用矢量字库),然后用anim动画动态设置lable位置
  • 上海1KM人口热力数据分享
  • 音频分类模型笔记
  • rust 从入门到精通之变量和常量
  • 杂记 04
  • 脑潜在进展:基于潜扩散模型的三维脑磁共振成像个体时空疾病进展研究|文献速递-深度学习人工智能医疗图像
  • python的课外学习生活活动系统
  • 视觉语言导航(13)——AIR-VLN 4.3
  • Mysql核心框架知识
  • 学习雪花算法
  • 冒泡排序——简单理解和使用