SPFA的拓展应用

news2024/12/26 20:42:31

spfa的拓展应用——负环

理论

  • 01分数规划
  • 负环:一个环边权之和小于零

求负环的基本方法,基于SPFA:

都是基于抽屉原理,如果超过n条边,那一定有两个点相同,那就一定存在一个环

(1) 统计每个点入队次数,如果某个点入队n次,则说明存在负环;

  • 本质是bellman_ford——O( n 2 n^2 n2)

(2) 统计当前每个点的最短路中所包含的边数,如果某点的最短路所包含的边数大于等于n,则说明也存在负环;

(3) 取巧做法trick:

  • 当所有点入队次数超过一定阈值时, 2 n 2n 2n,则我们认为图中有很大可能是存在负环的

初始化tips

  • 将起始点全部入队
  • 建立虚拟原点

基本模版

int d[N];

void spfa(){
  memset(d,0x3f,sizeof d);
  d[1]=0;
  st[1]=1;
  queue<int> q;
  
  while(q.size()){
    int t=q.front();
    q.pop();
    
    st[t]=false;
    for(int i=h[t];~i;i=ne[i]){
      int j=e[i];
      if(!st[j]){
        d[j]=min(d[t]+g[t][j],d[j]);
        st[j]=true;
      }
    }
  }
}

拓展模版

在基础之上增加一个cnt[N]用来判断负环

题单

1.虫洞

思考:

  • “表示存在一条从田地 𝑆 走到田地 𝐸 的虫洞,走过这条虫洞,可以回到 T T T 秒之前。”回到起始点之前,这里是将时间当作边的长度,比起始的长度还小,表明存在负环
  • 所以直接spfa开打
  • 打完单点spfa发现并没有告诉出发点,并且是说几片农场,有些农场可能并没有连通,全部点先入队一遍吧
#include<bits/stdc++.h>

using namespace std;
int f,n,m,w;
const int N=510,M=1e4+10;
int h[N],e[M],ne[M],weight[M],idx;
int d[N],st[N],cnt[N];

void add(int a,int b,int c){
  e[idx]=b,weight[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

bool spfa(){
  memset(d,0x3f,sizeof d);
  memset(st,0,sizeof st);
  memset(cnt,0,sizeof cnt);
  queue<int> q;
  d[0]=0;
  for(int i=1;i<=n;i++){
    add(0,i,0);
    add(i,0,0);
  }
  q.push(0);
  
  while(q.size()){
    int t=q.front();
    q.pop();
    st[t]=0;
    for(int i=h[t];i!=-1;i=ne[i]){
      int j=e[i];
      if(d[j]>d[t]+weight[i]){
        d[j]=d[t]+weight[i];
        cnt[j]=cnt[t]+1;
        //cout<<t<<' '<<j<<' '<<d[j]<<endl;
        if(cnt[j]>=n+1){
            
            return 1;
        }
        if(!st[j]){
          q.push(j);
          st[j]=1;
        }
      }
    }
  }
  return 0;
}

signed main(){
  cin>>f;
  while(f--){
    cin>>n>>m>>w;
    memset(h,-1,sizeof h);
    idx=0;
    for(int i=0;i<m;i++){
      int x,y,z;
      cin>>x>>y>>z;
      add(x,y,z),add(y,x,z);
    }
    for(int i=0;i<w;i++){
      int a,b,c;
      cin>>a>>b>>c;
      add(a,b,-c);
    }
    int res=spfa();
    if(!res) puts("NO");
    else puts("YES");
  }
  return 0;
}

不能直接用虚拟原点建边,这样会引入多一个点,很容易出现负环,虚拟原点到其他点的变长不好定义

如图:

image-20240629012754545

#include<bits/stdc++.h>

using namespace std;
int f,n,m,w;
const int N=510,M=1e4+10;
int h[N],e[M],ne[M],weight[M],idx;
int d[N],st[N],cnt[N];

void add(int a,int b,int c){
  e[idx]=b,weight[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

bool spfa(){
  memset(d,0,sizeof d);
  memset(st,0,sizeof st);
  memset(cnt,0,sizeof cnt);
  queue<int> q;
  
  for(int i=1;i<=n;i++){
    q.push(i);
    st[i]=1;
  }
  
  while(q.size()){
    int t=q.front();
    q.pop();
    st[t]=0;
    for(int i=h[t];i!=-1;i=ne[i]){
      int j=e[i];
      if(d[j]>d[t]+weight[i]){
        d[j]=d[t]+weight[i];
        cnt[j]=cnt[t]+1;
        //cout<<t<<' '<<j<<' '<<d[j]<<endl;
        if(cnt[j]>=n){
            return 1;
        }
        if(!st[j]){
          q.push(j);
          st[j]=1;
        }
      }
    }
  }
  return 0;
}

signed main(){
  scanf("%d",&f);
  while(f--){
    cin>>n>>m>>w;
    memset(h,-1,sizeof h);
    idx=0;//别忘了初始化idx
    for(int i=0;i<m;i++){
      int x,y,z;
      cin>>x>>y>>z;
      add(x,y,z),add(y,x,z);
    }
    for(int i=0;i<w;i++){
      int a,b,c;
      cin>>a>>b>>c;
      add(a,b,-c);
    }
    int res=spfa();
    if(!res) puts("NO");
    else puts("YES");
  }
  return 0;
}
2. 观光奶牛

第一眼:

  • “环上各点的权值之和”除以“环上各边的权值之和”第一次见

听y说:

  • 01分数规划问题

思考:

  • 怎么去体现最大?
    • 用mid去划分,因为 ∑ ( f i ) ∑ ( w i ) \frac{\sum(f_i)}{\sum(w_i)} (wi)(fi)有一个取值范围
  • 那要怎么找到满足大于mid的环?
    • 确切的说是如何找到环
    • 这个mid是用来生成新的边找到正环的
  • 注意”把点权放进边中“这句话在求最长路中的写法
#include<bits/stdc++.h>

using namespace std;
typedef pair<int,int> PII;
int n,m;
const int N=1e3+10,M=5e3+10;

int fa[N],f[N];
int h[N],hr[N],e[M],ne[M],w[M],idx;
int cnt[N],st[N];
double d[N];


void add(int a,int b,int c){
  e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

bool check(double mid){
  //其实d初不初始化都没太大关系,比较的只是相对关系,已经在
  //同一层变化的相对关系是不会发生变化的
  //memset(d,0,sizeof d);
  memset(cnt,0,sizeof cnt);
  memset(st,0,sizeof st);

  
  int q[N];
  int hh=0,tt=0;
 	for(int i=1;i<=n;i++){
    q[tt++]=i;
    st[i]=1;
  }
  while(hh!=tt){
    int t=q[hh++];
    if(hh==N) hh=0;
    
    st[t]=0;
    for(int i=h[t];i!=-1;i=ne[i]){
      int j=e[i];
      if(d[j]<d[t]+f[t]-mid*w[i]){
        d[j]=d[t]+f[t]-mid*w[i];
        cnt[j]=cnt[t]+1;
        if(cnt[j]>=n) return true;
        if(!st[j]){
          st[j]=1;
          q[tt++]=j;
          if(tt==N) tt=0;
        }
      }
    }
  }
  return false;
}

signed main(){
  cin>>n>>m;
  for(int i=1;i<=n;i++) cin>>f[i];
  memset(h,-1,sizeof h);
  for(int i=0;i<m;i++){
    int a,b,c;
    cin>>a>>b>>c;
    add(a,b,c);
  }
  
  double l=1,r=1001;
  while(r-l>1e-4){
    double mid=(l+r)/2;
    if(check(mid)) l=mid; 	//如果存在则说明答案在mid右边
    else r=mid;
  }
  printf("%.2f",l);
  return 0;
}
3. 单词环

第一眼:

  • 不太清楚为什么和求负环有关系
  • 和单词接龙会不会有关系
    • 相同的处理方式:处理两个串之间的?。。。。感觉又不行

思路:

  • 建图用对偶的方式,把首尾各两个字符组合成一个点,这样考虑最多情况也只有26*26种点,而边则依据字符串的长度来创建。
  • 同时当mid==0时可以做一层优化
    • 相当于减去零,环剩余边权之和如果仍不是正数,则说明一定没有正环
#include<bits/stdc++.h>

using namespace std;
const int N=700,M=1e5+10;
int h[N],e[M],ne[M],w[M],idx;
int n;
int st[N],cnt[N];
double d[N];

void add(int a,int b,int c){
  e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

bool check(double mid){
  memset(cnt,0,sizeof cnt);
  memset(st,0,sizeof st);
  
	int q[N];
  int hh=0,tt=0;
  for(int i=0;i<26*26;i++){
    q[tt++]=i;
    st[i]=1;
  }
  int count=0;
  while(hh!=tt){
    int t=q[hh++];
    if(hh==N) hh=0;
    st[t]=0;
    
    for(int i=h[t];i!=-1;i=ne[i]){
      int j=e[i];
      if(d[j]<d[t]+w[i]-mid){
        d[j]=d[t]+w[i]-mid;
        cnt[j]=cnt[t]+1;
    		if(cnt[j]>=N) return true;
        if(++count > 10000) return true;
        if(!st[j]){
          q[tt++]=j;
          if(tt==N) tt=0;
          st[j]=1;
        }
      }
    }
  }
  return false;
}

signed main(){
  while(cin>>n,n){
    memset(h,-1,sizeof h);
    idx=0;
    for(int i=0;i<n;i++){
      string s;
      cin>>s;
      int len=s.length();
      int a,b;
      if(len>=2){
        a=(s[0]-'a')*26+(s[1]-'a');
        b=(s[len-2]-'a')*26+(s[len-1]-'a');
        add(a,b,len); 
      }

    }
    if(!check(0)) puts("No solution");
    else{
      double l=1,r=1001;
      while(r-l>1e-4){
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
      }
      printf("%lf\n",r);
    }
  }
  
  
  return 0;
}

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

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

相关文章

linux 下neo4j的安装

一、neo4j简介 Neo4j 是一个高性能的 NoSQL 图形数据库,它将结构化数据存储在网络(从数学角度叫做图)上而不是表中。Neo4j 也可以被看作是一个高性能的图引擎,该引擎具有成熟数据库的所有特性。 neo4j与jdk版本对应 neo4j的版本需要与jdk版本相适配,否则容易出现安装失…

个人搭建cppreference网站

近日,由于购买的腾讯云服务器要过期了,之前在服务器搭建的cppreference也要重新搭建,故写下此文章 cppreference的访问速度也慢,故自己WSL子系统简单搭键一下是个不错的选择 环境准备 首先,自己先安装Nginx,在网上找安装教程即可下载cppreference网站资源包:https://pan.baidu…

Redis基础教程(一):redis数据类型

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

【sqlmap命令学习及测试dvwa_SQL_Injection】

文章目录 1.sqlmap命令及 不同级别探索 能否注入命令option1.1 low等级1.2 Medium等级1. 3 High等级 2. 注入流程2.1 数据库2.2 指定数据库表名2.3 指定表的 字段名2.4 内容2.5 当前用户信息2.6 用户密码2.7 其他 1.sqlmap命令及 不同级别探索 能否注入 命令option sqlmap -u…

无刷直流电机(BLDCM)位置识别SVPWM控制

无刷直流电机&#xff0c;即BLDCM&#xff0c;在各个行业应用非常广泛。我们最熟悉的是在四轴飞行器中的应用&#xff0c;其中的电机基本都是BLDCM。除此之外&#xff0c;汽车电子、家用电器、航空航天、办公自动化、机器人等领域都有重要应用。 梯形波/方波无刷直流电机被称为…

【爆肝34万字】从零开始学Python第2天: 判断语句【入门到放弃】

目录 前言判断语句True、False简单使用作用 比较运算符引入比较运算符的分类比较运算符的结果示例代码总结 逻辑运算符引入逻辑运算符的简单使用逻辑运算符与比较运算符一起使用特殊情况下的逻辑运算符 if 判断语句引入基本使用案例演示案例补充随堂练习 else 判断子句引入else…

2024年度总结:不可错过的隧道IP网站评估推荐

随着网络技术的飞速发展&#xff0c;隧道IP服务成为了许多企业和个人在进行网络活动时的得力助手。作为专业的测评团队&#xff0c;我们经过一整年的深入研究和测试&#xff0c;为大家带来了三款备受瞩目的隧道IP网站推荐——品易HTTP、极光HTTP和一G代理。接下来&#xff0c;我…

AI产品经理面试

把优秀当习惯把优秀当习惯肯定不是口头说说&#xff0c;那有什么判断标准吗&#xff1f; 当我做完一件事儿的时候&#xff0c;我会看它有没有突破我的舒适圈、能不能惊艳到我自己。这就是我的判断标准。 在自我介绍和经历介绍时&#xff0c;面试者应该注重以下几个方面&#xf…

字节码编程ASM之插桩调用其他类的静态方法

写在前面 源码 。 本文看下通过ASM如何实现插桩调用其他类的静态方法。 1&#xff1a;编码 假定有如下的类&#xff1a; public class PayController {public void pay(int userId, int payAmount) {System.out.println("用户&#xff1a;" userId ", 调用…

GPIO和PIN

文章目录 1 GPIO和Pin1.1 GPIO和Pin基础概念1.2 GPIO输入模式1.3 GPIO输出模式1.4 GPIO的HAL库1.4.1 一些HAL库表示1.4.2 HAL库常用GPIO函数1.4.3 GPIO点亮led灯程序例子 1 GPIO和Pin 1.1 GPIO和Pin基础概念 ​ 单片机有很多的引脚&#xff0c;为了操控每一个引脚&#xff0c…

ChatGPT之母:AI自动化将取代人类,创意性工作或将消失

目录 01 AI取代创意性工作的担忧 1.1 CTO说了啥 02 AI已开始大范围取代人类 01 AI取代创意性工作的担忧 几天前的采访中&#xff0c;OpenAI的CTO直言&#xff0c;AI可能会扼杀一些本来不应该存在的创意性工作。 近来一篇报道更是印证了这一观点。国外科技媒体的老板Miller用…

EC-R3588SPC 资料介绍

EC-R3588SPC 资料介绍 1 介绍1.1 简介1.2 资料链接1.3 硬件资源1.4 资源下载 2 升级固件2.1 启动模式说明2.1.1 前言2.1.2 如何获取固件2.1.3 升级方法2.1.4 启动媒体2.1.5 引导模式2.1.5.1 正常模式2.1.5.2 装载机模式2.1.5.3 MaskRom模式 2.2 通过USB线升级固件2.2.1 介绍2.2…

树 | 第6章 | Java版大话数据结构 | 1.7w字长文 | 二叉树 | 哈夫曼树 | 二叉树遍历 | 构造二叉树 | LeetCode练习

&#x1f4cc;本篇分享的大话数据结构中&#x1f384;树&#x1f384;这一章的知识点&#xff0c;在此基础上&#xff0c;增加了练习题帮助大家理解一些重要的概念✅&#xff1b;同时&#xff0c;由于原文使用的C语言代码&#xff0c;不利于学习Java语言的同学实践&#xff0c;…

endswith()方法——是否以指定子字符串结尾

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 endswith()方法用于检索字符串是否以指定子字符串结尾。如果是则返回True&#xff0c;否则返回False。endswith()方法的语法格式如下&…

GenAI 用于客户支持 — 第 1 部分:构建我们的概念验证

作者&#xff1a;来自 Elastic Chris Blaisure 欢迎来到 Inside Elastic 博客系列&#xff0c;我们将展示 Elastic 的内部运营如何解决实际业务挑战。本系列将揭示我们将生成式 AI&#xff08;gererative AI - GenAI&#xff09;集成到客户成功和支持运营中的历程&#xff0c;让…

【C++】类、静态、枚举、重载、多态、继承、重写、虚函数

五、类 面向对象编程是一个巨大的编程范式。C中的类class就是基于对象的程序设计。 我们可以用类来定义一个新的类型&#xff0c;这些新类型就可以像内置类型一样使用。 内置类型颗粒度太太小&#xff0c;现实需求又非常复杂&#xff0c;这就需要我们把内置类型适度的进行拼搭…

在Redis中使用Lua脚本实现多条命令的原子性操作

Redis作为一个高性能的键值对数据库&#xff0c;被广泛应用于各种场景。然而&#xff0c;在某些情况下&#xff0c;我们需要执行一系列Redis命令&#xff0c;并确保这些命令的原子性。这时&#xff0c;Lua脚本就成为了一个非常实用的解决方案。 问题的提出 假设我们有一个计数…

Redis为什么设计多个数据库

​关于Redis的知识前面已经介绍过很多了,但有个点没有讲,那就是一个Redis的实例并不是只有一个数据库,一般情况下,默认是Databases 0。 一 内部结构 设计如下: Redis 的源码中定义了 redisDb 结构体来表示单个数据库。这个结构有若干重要字段,比如: dict:该字段存储了…

Redis-实战篇-缓存更新策略(内存淘汰、超时剔除、主动更新)

文章目录 1、缓存更新策略1.1、内存淘汰1.2、超时剔除1.3、主动更新 2、业务场景&#xff1a;3、主动更新在企业中业务实现有三种方式3.1、Cache Aside Pattern3.1.1、操作缓存和数据库时有三个问题需要考虑&#xff1a;3.1.1.1、删除缓存还是更新缓存&#xff1f;3.1.1.2、如何…

信息学奥赛初赛天天练-39-CSP-J2021基础题-哈夫曼树、哈夫曼编码、贪心算法、满二叉树、完全二叉树、前中后缀表达式转换

PDF文档公众号回复关键字:20240629 2022 CSP-J 选择题 单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 5.对于入栈顺序为a,b,c,d,e的序列&#xff0c;下列( )不合法的出栈序列 A. a&#xff0c;b&a…