【蓝桥杯集训15】求最短路存在负权边——spaf算法(3 / 4)

news2024/11/17 3:25:21

——SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称

单源最短路,且图中没有负环就可以用spfa

目录

spaf求最短路模板

852. spfa判断负环 

341. 最优贸易 - spfa + 双向建图

3305. 作物杂交 - 


spaf求最短路模板

只有当一个点的前驱结点更新了,该节点才会得到更新

因此只需要创建一个队列每一次加入距离被更新的结点

队列存的是待更新的节点——取出队列里的节点会更新它的后续节点

已经在队列的节点不需要重复入队,可以用st数组标记已入队节点

spfa算法步骤:

  • 建立队列,队列初始只有节点1
  • 取出队头节点x,取消该点标记,遍历x所有出边(x,y,z),若dist[y]>dist[x]+w,则更新最短路dist[y]=dist[x]+w,若y不在队列中,让y入队并标记
  • 重复上述步骤,直到队列为空
  • 注:dist[x]存1→x的最短路径长度  st[x]标记x节点是否在队列中

活动 - AcWing

题目:

给定n个点m条边的带权有向图,图中可能存在重边和自环,边权可能为负数

请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 impossible。

数据保证不存在负权回路。

/*
    *道阻且长,行则将至*
    author:Roye_ack
*/
import java.util.*;
import java.io.*;
import java.math.*;
 
class Main
{
    static PrintWriter wt=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
    static int N=100010;
    static int n,m,idx;
    static int[] h=new int[N],e=new int[N],ne=new int[N],w=new int[N];
    static int[] dist=new int[N];
    static int[] st=new int[N];
    
    public static void add(int a,int b,int c)
    {
        e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
    }
    
    public static int spaf()
    {
        Queue<Integer> q=new LinkedList<>();
        q.offer(1);
        Arrays.fill(dist,0x3f3f3f3f);
        dist[1]=0;
        st[1]=1;
        
        while(!q.isEmpty())
        {
            var t=q.poll();
            st[t]=0;
            
            for(int i=h[t];i!=-1;i=ne[i])
            {
                int j=e[i];
                if(dist[j]>dist[t]+w[i])
                {
                    dist[j]=dist[t]+w[i];
                    if(st[j]==0) //如果当前队列里不存在该节点 则入队并标记
                    {
                        q.offer(j);
                        st[j]=1;
                    }
                }
            }
        }
        return dist[n];
    }
    
    public static void main(String[] args) throws IOException
    {
        n=rd.nextInt();
        m=rd.nextInt();
        
        Arrays.fill(h,-1);
        
        while(m-->0)
        {
            int a=rd.nextInt(),b=rd.nextInt(),c=rd.nextInt();
            add(a,b,c);
        }
        
        int res=spaf();
        if(res==0x3f3f3f3f) wt.print("impossible");
        else wt.print(res);
 
        wt.flush();
    }
    
    static class rd
    {
        static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        static StringTokenizer tk=new StringTokenizer("");
        
        static String nextLine() throws IOException
        {
            return bf.readLine();
        }
        
        static String next() throws IOException
        {
            while(!tk.hasMoreTokens()) tk=new StringTokenizer(bf.readLine());
            return tk.nextToken();
        }
        
        static int nextInt() throws IOException
        {
            return Integer.parseInt(next());
        }
        
        static double nextDouble() throws IOException
        {
            return Double.parseDouble(next());
        }
        
        static long nextLong() throws IOException
        {
            return Long.parseLong(next());
        }
        
        static BigInteger nextBig() throws IOException
        {
            BigInteger d=new BigInteger(rd.nextLine());
            return d;
        }
    }
}
 
class PII
{
    int x,y;
    PII(int x,int y)
    {
        this.x=x;
        this.y=y;
    }
}

852. spfa判断负环 

活动 - AcWing

题目:

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数

请你判断图中是否存在负权回路

思路:

在spfa基础上添加一个cnt数组,cnt[x]存1→x经过的边数

边数cnt的更新方式:cnt[x]=cnt[t]+1(1到t的边数+1)

判断负环原理:

  • 如果cnt[x]≥n,说明1~x至少经过了n+1个点,由抽屉原理可知至少两个点的编号一样
  • 由于只有当dist[x]<dist[t]+w[i]才会更新cnt边数,因此w[i]必定是负数
  • 综上可判断图中存在负环
/*
    *道阻且长,行则将至*
    author:Roye_ack
*/
import java.util.*;
import java.io.*;
import java.math.*;
 
class Main
{
    static PrintWriter wt=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
    static int N=100010;
    static int n,m,idx;
    static int[] h=new int[N],e=new int[N],ne=new int[N],w=new int[N];
    static int[] dist=new int[N],cnt=new int[N]; //cnt用于存边数
    static int[] st=new int[N];
    
    public static void add(int a,int b,int c)
    {
        e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
    }
    
    public static boolean spaf()
    {
        Queue<Integer> q=new LinkedList<>();
        //这里不用初始化dist数组为 正无穷/初始化的原因是:如果存在负环,那么dist不管初始化为多少,都会被更新
        
        for(int i=1;i<=n;i++) //该题是判断是否存在负环,并非判断是否存在从1开始的负环,因此需要将所有的点都加入队列中,更新周围的点
        {
            q.offer(i);
            st[i]=1;
        }
        
        while(!q.isEmpty())
        {
            var t=q.poll();
            st[t]=0;
            
            for(int i=h[t];i!=-1;i=ne[i])
            {
                int j=e[i];
                if(dist[j]>dist[t]+w[i])
                {
                    dist[j]=dist[t]+w[i];
                    cnt[j]=cnt[t]+1;
                    
                    if(cnt[j]>=n) return true;
                    
                    if(st[j]==0)
                    {
                        q.offer(j);
                        st[j]=1;
                    }
                }
            }
        }
        return false;
        
    }
    
    public static void main(String[] args) throws IOException
    {
        n=rd.nextInt();
        m=rd.nextInt();
        
        Arrays.fill(h,-1);
        
        while(m-->0)
        {
            int a=rd.nextInt(),b=rd.nextInt(),c=rd.nextInt();
            add(a,b,c);
        }
        
        if(spaf()) wt.print("Yes");
        else wt.print("No");
 
        wt.flush();
    }
    
    static class rd
    {
        static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        static StringTokenizer tk=new StringTokenizer("");
        
        static String nextLine() throws IOException
        {
            return bf.readLine();
        }
        
        static String next() throws IOException
        {
            while(!tk.hasMoreTokens()) tk=new StringTokenizer(bf.readLine());
            return tk.nextToken();
        }
        
        static int nextInt() throws IOException
        {
            return Integer.parseInt(next());
        }
        
        static double nextDouble() throws IOException
        {
            return Double.parseDouble(next());
        }
        
        static long nextLong() throws IOException
        {
            return Long.parseLong(next());
        }
        
        static BigInteger nextBig() throws IOException
        {
            BigInteger d=new BigInteger(rd.nextLine());
            return d;
        }
    }
}
 
class PII
{
    int x,y;
    PII(int x,int y)
    {
        this.x=x;
        this.y=y;
    }
}

341. 最优贸易 - spfa + 双向建图

活动 - AcWing

题目:

n个城市m条边,1代表x到y单向通道,2代表x到y的双向通道

每个城市都有一个商品的价格,一人从1号点出发,在某个城市买一个商品

在另一个城市卖出,该交易只进行一次

问最大赚取的差价为多少?

思路:

为何不能用dijkstra算法?

如果当前 dmin[i] 最小的点是 5,那么有可能存在边 5-> 6, 6-> 7, 7-> 5,假设当前 dmin[5] = 10,则有可能存在 6 的价格是11, 但 7 的价格是3,那么 dmin[5] 的值就应该被更新成3,因此当前最小值也不一定是最终最小值,所以dijkstra算法并不适用,我们只能采用 spfa 算法

dijkstra算法的特性保证后面出现的点都比前面出现的差,不能及时更新最小值

思路总体就是:1~i正向取最小 n~i反向取最大 枚举所有点作为中间点i 求出差值的最大值

  • 从 1 走到 i 的过程中,买入水晶球的最低价格 dmin[i];
  • 从 i 走到 n 的过程中,卖出水晶球的最高价格 dmax[i];

然后枚举每个城市作为买卖的中间城市,求出 dmax[i] - dmin[i] 的最大值

为什么不能正向求出i号点到n号点求出最大价格?

因为并不是所有点到终点都是可达的,必须保证从1号点走到i号点时,i号点一定能走到n号点

/*
    *道阻且长,行则将至*
    author:Roye_ack
*/
import java.util.*;
import java.io.*;
import java.math.*;
 
class Main
{
    static PrintWriter wt=new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
    static int N=100010,M=5*N;
    static int n,m,idx;
    static int[] h=new int[N],rh=new int[N],e=new int[M],ne=new int[M];
    static int[] w=new int[N];
    static int[] st=new int[N];
    static int[] dmin=new int[N],dmax=new int[N];
    
    public static void add(int[]h,int a,int b)
    {
        e[idx]=b;ne[idx]=h[a];h[a]=idx++;
    }
    
    public static void spfa(int[] d,int start,int[] h,boolean flag)
    {
        Queue<Integer> q=new LinkedList<>();
        
        if(flag) Arrays.fill(d,0x3f3f3f3f);
        
        q.offer(start);
        d[start]=w[start];
        st[start]=1;
        
        while(!q.isEmpty())
        {
            var t=q.poll();
            st[t]=0;
            
            for(int i=h[t];i!=-1;i=ne[i])
            {
                int j=e[i];
                if(flag&&d[j]>Math.min(d[t],w[j])||!flag&&d[j]<Math.max(d[t],w[j]))
                {
                    if(flag) d[j]=Math.min(d[t],w[j]);
                    else d[j]=Math.max(d[t],w[j]);
                    
                    if(st[j]==0)
                    {
                        st[j]=1;
                        q.offer(j);
                    }
                }
            }
        }
    }
    
    public static void main(String[] args) throws IOException
    {
        n=rd.nextInt();
        m=rd.nextInt();
        
        Arrays.fill(h,-1);
        Arrays.fill(rh,-1);
        
        for(int i=1;i<=n;i++) w[i]=rd.nextInt();
        
        while(m-->0)
        {
            int a=rd.nextInt(),b=rd.nextInt(),c=rd.nextInt();
            add(h,a,b); //便于计算1~i点
            add(rh,b,a); //便于计算n~i点
            if(c==2) 
            {
                add(h,b,a);
                add(rh,a,b);
            }
        }
        
        spfa(dmin,1,h,true);
        spfa(dmax,n,rh,false);
 
        int res=0;
        for(int i=1;i<=n;i++) res=Math.max(res,dmax[i]-dmin[i]);
        wt.print(res);
        wt.flush();
    }
    
    static class rd
    {
        static BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        static StringTokenizer tk=new StringTokenizer("");
        
        static String nextLine() throws IOException
        {
            return bf.readLine();
        }
        
        static String next() throws IOException
        {
            while(!tk.hasMoreTokens()) tk=new StringTokenizer(bf.readLine());
            return tk.nextToken();
        }
        
        static int nextInt() throws IOException
        {
            return Integer.parseInt(next());
        }
        
        static double nextDouble() throws IOException
        {
            return Double.parseDouble(next());
        }
        
        static long nextLong() throws IOException
        {
            return Long.parseLong(next());
        }
        
        static BigInteger nextBig() throws IOException
        {
            BigInteger d=new BigInteger(rd.nextLine());
            return d;
        }
    }
}
 
class PII
{
    int x,y;
    PII(int x,int y)
    {
        this.x=x;
        this.y=y;
    }
}

3305. 作物杂交 - 

3305. 作物杂交 - AcWing题库

题目:

思路:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/390679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多线程共享数据结构的无锁释放

目录背景问题共享结构的无锁释放对比ref-cntrcuepoch-based reclamhazard pointer: 冒险指针结构原理正确性保证范例参考背景 多线程共享一个数据结构。 共享数据结构&#xff0c;可以做到节约内存。 但是多线程共享&#xff0c;可能会有问题&#xff0c;比如同步的问题。 问…

(Android-RTC-9)PeerConnectionFactory

开篇前瞎扯。很久没发技术文章了&#xff0c;此文一直放着草稿箱没有完成&#xff0c;感觉自己在家庭和工作中找到了拖延的借口&#xff0c;开始慢慢变得懒惰了&#xff0c;那是万万不行的。恰逢2023开年ChatGPT的爆火&#xff0c;更让我这些普通程序员危机感瞬间飙升&#xff…

安全规约第一章

文章目录传统密码和现代密码的区别古典密码近代密码现代密码定义模型证明现代密码学CryptographyCryptanalysisCryptosystemScheme如何定义算法步骤第一步&#xff0c;搞清楚安全服务目标第二步&#xff0c;计算过程中需要几方的参与需要几个算法描述它算法命名谁来运行哪一个算…

03_Apache Pulsar的Local与分布式集群构建、Pulsar的分布式集群模式、Pulsar的分布式集群模式构建\启动\测试

1.3.Apache Pulsar的Local与分布式集群构建 1.3.1 Apache Pulsar的Local模式构建 1.3.1.1.Apache Pulsar的Local模式基本使用 1.3.2.Apache Pulsar的分布式集群模式 1.3.2.1.安装zookeeper集群 1.3.3.Apache Pulsar的分布式集群模式构建 1.3.4.Apache Pulsar的分布式集群模式启…

Sparx Systems Pro Cloud Server crack

Sparx Systems Pro Cloud Server crack 云服务器 添加了新的“OSLC会话超时”设置&#xff0c;以配置OSLC用户将注销的最长非活动时间。 改进了对重复SQL列名的处理。 FLS&#xff1a;为“组”添加了对其他Microsoft Active Directory名称格式的支持。 云配置客户端 在扩展服务…

香橙派5使用NPU加速yolov5的实时视频推理(二)

三、将best.onnx转为RKNN格式 这一步就需要我们进入到Ubuntu20.04系统中了&#xff0c;我的Ubuntu系统中已经下载好了anaconda&#xff0c;使用anaconda的好处就是可以方便的安装一些库&#xff0c;而且还可以利用conda来配置虚拟环境&#xff0c;做到环境与环境之间相互独立。…

SpringCloud Alibaba入门

作为微服务刚入门的小白&#xff0c;不足之处请多多指教 1. Cloud Alibaba简介2.Nacos简介和下载3.Nacos安装4.Nacos之服务提供者注册5.Nacos之服务消费者注册和负载6.Nacos服务注册中心对比提升7.Nacos之服务配置中心8.Nacos之命名空间分组和DataID三者关系9.Nacos之DataID配…

如何写好单测

1、为什么要写单测&#xff1f; 单测即单元测试&#xff08;Unit Test&#xff09;&#xff0c;是对软件的基本组成单元进行的测试&#xff0c;比如函数、过程或者类的方法。其意义是&#xff1a; 功能自测&#xff0c;发现功能缺陷自我Code Review测试驱动开发促进代码重构并…

File、递归、IO流(一)、IO流(二)

目录 ​File类概述 File类的常用API 判断文件类型、获取文件信息 创建文件、删除文件功能 遍历文件夹 方法递归 递归的形式和特点 递归的算法流程、核心要素 递归常见案例 递归的经典问题 非规律化递归案例-文件搜索 非规律化递归案例-啤酒问题 字符集 常见字符集…

美团二面经历——如何设计一个百万人抽奖系统?

文章目录 导图V0——单体架构V1——负载均衡V2——服务限流防止用户重复抽奖拦截无效流量服务降级和服务熔断V3 同步状态V4线程优化V5业务逻辑V6流量削峰通用思路单一职责URL动态加密静态资源——CDN服务限流数据预热削峰填谷导图 导图按照由浅入深的方式进行讲解,架构从来不是…

西电计算机组成原理(计组)核心考点汇总(期末真题+核心考点)

文章目录前言一、真题概览1.1 计组1历年真题1.2 计组2历年真题二、知识点说明2.1 计组12.1.1 冯诺依曼计算机组成和特点2.1.2 复杂指令系统计算机和特点2.1.3 精简指令系统计算机的特点2.1.4 指令长度的影响因素2.1.5 控制器2.1.6 微指令特性2.2 计组22.2.1 SMP特点与优点2.2.2…

QML动态对象管理

QML中有多种方式来动态创建和管理QML对象&#xff1a; Loader &#xff08;加载器&#xff09;Repeater&#xff08;复制器&#xff09;ListView&#xff0c;GridWiew&#xff0c;PethView&#xff08;视图&#xff09; &#xff08;之后会介绍&#xff09;使用加载器&#xff…

剖析G1 垃圾回收器

简单回顾 在Java当中&#xff0c;程序员在编写代码的时候只需要创建对象&#xff0c;从来不需要考虑将对象进行释放&#xff0c;这是因为Java中对象的垃圾回收全部由JVM替你完成了(所有的岁月静好都不过是有人替你负重前行)。 而JVM的垃圾回收由垃圾回收器来负责&#xff0c;在…

刷题记录:牛客NC200179Colorful Tree 奇奇怪怪的dfs序

传送门:牛客 题目描述: A tree structure with some colors associated with its vertices and a sequence of commands on it are given. A command is either an update operation or a query on the tree. Each of the update operations changes the color of a specifi…

论文阅读 - End-to-End Wireframe Parsing

文章目录1 概述2 L-CNN2.1 整体架构2.2 backbone2.3 juction proposal module2.4 line sample module2.5 line verificatoin module3 评价指标参考资料1 概述 本文是ICCV2019的一篇论文&#xff0c;核心是提出了一种简单的end-to-end的two-stage的检测图像中线段的方法。同时&…

192、【动态规划】leetcode ——64. 最小路径和:回溯法+动态规划(C++版本)

题目描述 原题链接&#xff1a;64. 最小路径和 解题思路 &#xff08;1&#xff09;回溯法 分别向右或下进行探查 class Solution { public:int res INT_MAX;void backtracking(vector<vector<int>>& grid, int x, int y, int pathSum) {// 超出边界&…

高可用 - 08 Keepalived集群中Master和Backup角色选举策略

文章目录概述实例说明“weight”值为正数“weight”值为负数总结概述 在Keepalived集群中&#xff0c;其实并没有严格意义上的主、备节点&#xff0c;虽然可以在Keepalived配置文件中设置“state”选项为“MASTER”状态&#xff0c;但是这并不意味着此节点一直就是Master角色。…

Python实现人脸识别,进行视频跟踪打码,羞羞的画面统统打上马赛克

哈喽兄弟们&#xff0c;我是轻松~ 今天我们来实现用Python自动对视频打马赛克前言准备工作代码实战效果展示最后前言 事情是这样的&#xff0c;昨天去表弟家&#xff0c;用了下他的电脑&#xff0c;不小心点到了他硬盘里隐藏的秘密&#xff0c;本来我只需要用几分钟电脑的&…

第一章初识Linux

文章目录Linux简介LInux的应用领域Linux OS和各种发行版的关系Linux和Unix的关系Linux相关环境配置图解VM和Linux的关系Linux自定义分三个区VMware网络连接的三种模式桥接模式NAT模式主机模式VMware快照功能Linux的操作方式Linux的目录结构各种Linux发行版本的常见目录注意事项…

GO进阶(5) 垃圾回收机制

一、前言 1、垃圾回收背景 编程语言通常会使用手动和自动两种方式管理内存&#xff0c;C、C 以及 Rust 等编程语言使用手动的方式管理内存&#xff0c;工程师需要主动申请或者释放内存&#xff1b;而 Python、Ruby、Java 和 Go 等语言使用自动的内存管理系统&#xff0c;一般都…