首页 > 其他分享 >【学习笔记】差分约束

【学习笔记】差分约束

时间:2024-01-27 21:12:02浏览次数:35  
标签:le luogu 笔记 约束 负环 vis 差分 dis

前言

2024.1.27 \(huge\) 在讲不要忽略算法的细节时,以最短路和差分约束为例子。发现自己差分约束忘得差不多了,于是就有了这篇博客。

负环

  • 在一张图中,若存在一条边权之和为负数的回路,则称这个回路为负环。在一张图中,若存在一条边权之和为正数的回路,则称这个回路为正环。
  • 如果一张图中存在负环,则可以表现为在 \(SPFA\) 的迭代过程中,无论经过多少次迭代,总存在一条有向边 \((x,y,z)\) 满足 \(dis_{s,y}>dis_{s,x}+z\) ,其中 \(s\) 为起点,从而使得 \(SPFA\) 的迭代永远不能结束。
  • 代码实现
    • 判定最短路径包含的边数
      • 设 \(cnt_{s,x}\) 表示从起点 \(s\) 到 \(x\) 的最短路径包含的边数。规定 \(cnt_{s,s}=0\) 。当执行 \(dis_{s,y}=dis_{s,x}+z\) 时,同时更新 \(cnt_{s,y}=cnt_{s,x}+1\) 。此时如果有 \(cnt_{s,y} \ge n\) ,则说明图中存在从起点 \(s\) 出发能到达的负环。若迭代能够完成,即算法正常结束,则说明图中不存在从起点 \(s\) 出发能到达的负环。
        bool spfa(int s,int n)
        {
        	int i,x;
        	memset(vis,0,sizeof(vis));
        	memset(dis,0x3f,sizeof(dis));
        	queue<int>q;
        	q.push(s);
        	dis[s]=0;
        	vis[s]=1;
        	while(q.empty()==0)
        	{
        		x=q.front();
        		vis[x]=0;
        		q.pop();
        		for(i=head[x];i!=0;i=e[i].nxt)
        		{
        			if(dis[e[i].to]>dis[x]+e[i].w)
        			{
        				dis[e[i].to]=dis[x]+e[i].w;
        				num[e[i].to]=num[x]+1;
        				if(num[e[i].to]>=n)
        				{
        					return false;//若返回false,则说明图中有负环
        				}
        				if(vis[e[i].to]==0)
        				{
        					q.push(e[i].to);
        					vis[e[i].to]=1;
        				}
        			}
        		}
        	}
        	return true;//若返回true,则说明图中没有负环
        }
        
    • 卡时做法
      • 对于上面的判定最短路径包含的边数做法,可以根据运行时间限制,给 \(cnt_{y}\) 设定一个上界,超出时直接判定存在负环。
        ......//剩余代码同上
        if(num[e[i].to]>=MAX_N)//MAX_N为设定的上界
        {
        	return false;//若返回false,则说明图中有负环
        }
        ......//剩余代码同上
        
    • 将 \(SPFA\) 从基于 \(BFS\) 实现改为基于 \(DFS\) 实现
  • 例题

差分约束

  • 差分约束系统是由 \(m\) 个 \(n\) 元一次不等式组成的 \(n\) 元一次不等式组,形如 \(\begin{cases}x_{a_{1}}-x_{b_{1}} \le c_{1}\\x_{a_{2}}-x_{b_{2}} \le c_{2} \\ \dots \\ x_{a_{m}}-x_{b_{m}} \le c_{m}\end{cases}\) ,其中对于每一个 \(i(1 \le i \le n)\) 均有 \(1 \le a_{i},b_{i} \le n\) 。我们要解决的问题是:求一组解 \(\begin{cases}x_{1}=ans_{1} \\ x_{2}=ans_{2} \\ \dots \\ x_{n}=ans_{n}\end{cases}\) ,使得所有的不等式均成立或给出无解信息。
  • 代码实现
    • 对于第 \(i(1 \le i \le m)\) 个不等式 \(x_{a_{i}}-x_{b_{i}} \le c_{i}\) ,可以变形为 \(x_{a_{i}} \le x_{b_{i}}+c_{i}\) 。这与三角形不等式 \(dis_{s,y} \le dis_{s,x}+z\) 十分相似。因此我们可以把变量 \(x_{a_{i}}\) 看做有向图中的一个节点 \(a_{i}\) ,从节点 \(b_{i}\) 向节点 \(a_{i}\) 连一条 \(c_{i}\) 的有向边。
      • 由不等式基本性质,容易有若 \(\begin{cases}x_{1}=ans_{1} \\ x_{2}=ans_{2} \\ \dots \\ x_{n}=ans_{n}\end{cases}\) 是原差分约束系统的一组解,则 \(\begin{cases}x_{1}=ans_{1}+d \\ x_{2}=ans_{2}+d \\ \dots \\ x_{n}=ans_{n}+d\end{cases}\) 也是原差分约束系统的一组解,其中 \(d\) 为常数。
      • 如果题目要求求非负整数解或通过如上的构图方式所构出的图不连通时,需要加入一个超级源点 \(0\) ,并令 \(x_{0}=0\) ,同时原差分约束系统增加了 \(n\) 个 \(n\) 元一次不等式 \(x_{a_{i}}-x_{0} \le 0\) 。
    • 设 \(dis_{0,0}=0\) ,以 \(0\) 为起点求单源最短路。若图中存在负环,则给定的差分约束系统无解;否则, \(\begin{cases}x_{1}=dis_{1} \\ x_{2}=dis_{2} \\ \dots \\ x_{n}=dis_{n}\end{cases}\) 为原差分约束系统的一组解。
      • 另外,若对于任意一个 \(i\) 有 \(x_{a_{i}}-x_{b_{i}} \ge c_{i}\) 可以从节点 \(b_{i}\) 向节点 \(a_{i}\) 连一条 \(c_{i}\) 的有向边,求单源最长路,判定有无解变为是否存在正环;或者可以转化为 \(x_{b_{i}}-x_{a_{i}} \le -c_{i}\) ,然后按照上面的算法求解。
        • 同样地,若对于任意一个 \(i\) 有 \(x_{a_{i}}-x_{b_{i}} \le c_{i}\) 也可以转化为 \(x_{b_{i}}-x_{a_{i}} \ge -c_{i}\) 。
  • 例题
    • luogu P5960 【模板】差分约束
      void spfa(ll s,ll n)//单源最短路找负环
      {
      	ll i,x;
      	memset(vis,0,sizeof(vis));
      	memset(dis,0x3f,sizeof(dis));
      	queue<ll>q;
      	q.push(s);
      	dis[s]=0;
      	vis[s]=1;
      	while(q.empty()==0)
      	{
      		x=q.front();
      		for(i=head[x];i!=0;i=e[i].nxt)
      		{
      			if(dis[e[i].to]>dis[x]+e[i].w)
      			{
      				dis[e[i].to]=dis[x]+e[i].w;
      				num[e[i].to]=num[x]+1;
      				if(num[e[i].to]>=n+1)
      				{
      					cout<<"NO"<<endl;
      					exit(0);
      				}
      				if(vis[e[i].to]==0)
      				{
      					q.push(e[i].to);
      					vis[e[i].to]=1;
      				}
      			}
      		}
      		vis[x]=0;
      		q.pop();
      	}
      }
      int main()
      {
      	ll n,m,u,v,w,i;
      	cin>>n>>m;
      	for(i=1;i<=n;i++)
      	{
      		add(0,i,0);
      	}
      	for(i=1;i<=m;i++)
      	{
      		cin>>u>>v>>w;
      		add(v,u,w);
      	}
      	spfa(0,n);
      	for(i=1;i<=n;i++)
      	{
      		cout<<dis[i]<<" ";
      	}
      	return 0;
      }
      
      void spfa(ll s,ll n)//单源最长路找正环
      {
      	ll i,x;
      	memset(vis,0,sizeof(vis));
      	memset(dis,-0x3f,sizeof(dis));
      	queue<ll>q;
      	q.push(s);
      	dis[s]=0;
      	vis[s]=1;
      	while(q.empty()==0)
      	{
      		x=q.front();
      		for(i=head[x];i!=0;i=e[i].nxt)
      		{
      			if(dis[e[i].to]<dis[x]+e[i].w)
      			{
      				dis[e[i].to]=dis[x]+e[i].w;
      				num[e[i].to]=num[x]+1;
      				if(num[e[i].to]>=n+1)
      				{
      					cout<<"NO"<<endl;
      					exit(0);
      				}
      				if(vis[e[i].to]==0)
      				{
      					q.push(e[i].to);
      					vis[e[i].to]=1;
      				}
      			}
      		}
      		vis[x]=0;
      		q.pop();
      	}
      }
      int main()
      {
      	ll n,m,u,v,w,i;
      	cin>>n>>m;
      	for(i=1;i<=n;i++)
      	{
      		add(0,i,0);
      	}
      	for(i=1;i<=m;i++)
      	{
      		cin>>u>>v>>w;
      		add(u,v,-w);
      	}
      	spfa(0,n);
      	for(i=1;i<=n;i++)
      	{
      		cout<<dis[i]<<" ";
      	}
      	return 0;
      }
      
      • 以上两种不同的写法均可。
        • 以下题目均使用单源最长路找正环求解。
    • luogu P1260 工程规划
    • tgHZOJ 183. 小K的农场
      • 数据弱化版:BZOJ 3436.小K的农场 | luogu P1993 小 K 的农场
      • 本题因数据经过特殊的构造,卡掉了基于 \(BFS\) 实现的 \(SPFA\) 。故需要使用 deque 优化 \(SPFA\) 或使用 \(DFS\) 式的 \(SPFA\)。但是因为数据较水,使用卡时做法也可通过。
    • luogu P3275 [SCOI2011] 糖果
    • luogu P4878 [USACO05DEC] Layout G
    • SP116 INTERVAL - Intervals
    • LibreOJ 10088. 「一本通 3.4 例 2」出纳员问题

参考资料

《算法竞赛进阶指南》——李煜东

Oi-WiKi

我的无优化 SPFA 果然有问题

标签:le,luogu,笔记,约束,负环,vis,差分,dis
From: https://www.cnblogs.com/The-Shadow-Dragon/p/17991944

相关文章

  • 智能车学习笔记/备忘录乱写 | 地平线篇
    前言持续更新中!校内赛打完分到地平线组了,好多新东西,写篇笔记记录。比赛使用OriginBot车模,车模搭载Ubuntu系统。以后估计主要在Ubuntu虚拟机上工作。现在需要先学习ROS2和FoxGlove。一些很基础或者很好记的东西就不写了。常用网址比赛简介(场地、车模、规则等)FoxGlove教程Or......
  • 2024/1/27学习进度笔记
    1)NLP基本概念①NLP(NaturalLanguageProcessing),也就是人们常说的「自然语言处理」,就是研究如何让计算机读懂人类语言,即将人的自然语言转换为计算机可以阅读的指令。②分词是NLP任务的一个起始,分词的好坏会影响整体模型的好坏。并且分词不一样,语义不一样。1.中国北京大......
  • HTML笔记
    1.HTML笔记1.1HTML文件1.1.1文档声明<!Doctypehtml>1.1.2.基本页面模板<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"/><!--媒体设置字符集设置--><metaname="viewport"content=&q......
  • MAC地址的格式与分类(学习笔记)
    Mac地址一.明晰概念MAC地址是以太网的MAC子层所使用的地址,是设备在以太网中的物理标识,在以太网中是用来实现多媒体接入控制(MediaAccessControl也是命名的由来),如同学生的学号,校园内可以通过学号(类似于MAC地址)来找到某个唯一确定的学生。学习时的收获:在学习时尽管查阅了许多......
  • 前缀和与差分典题
    includeusingnamespacestd;constintN=1010;inta[N][N],b[N][N];voidinsert(intx1,inty1,intx2,inty2,intc){b[x1][y1]+=c;b[x2+1][y1]-=c;b[x1][y2+1]-=c;b[x2+1][y2+1]+=c;}intmain(){intn,m,q,w;scanf("%d%d%d",&n,&m,&......
  • Node.js笔记
    第一篇 一、Node.js模块:模块使用npm运行管理。events:事件模块,提供事件触发和事件监听功能。util:核心功能模块,用于弥补核心JS功能的不足。fs:文件操作模块,提供文件操作APIhttp:Web协议模块,提供Web协议交互功能express:Web框架,用于快速构建Web应用服务vm:沙箱模块,用于提......
  • CSAPP学习笔记——Chapter10,11 系统级I/O与网络编程
    CSAPP学习笔记——Chapter10,11系统级I/O与网络编程Chapter10系统级I/O系统级I/O这一章的内容,主要可以通过这张图概括:UnixI/O模型是在操作系统内核中实现的。应用程序可以通过诸如open、close、lseek、read、write和stat这样的函数来访UnixI/O。较高级别的RIO和标......
  • 最小表示法&Manacher学习笔记+杂题
    字符串系列前言:孩子从小就自卑。四、最小表示法&Manacher学习笔记+杂题相关题单:戳我1.最小表示法最小表示法是用于解决字符串最小表示问题的方法。(1)字符串的最小表示:字符串\(s\)的最小表示为与\(s\)循环同构的所有字符串中字典序最小的字符串。循环同构指的是当字符......
  • 最小表示法&Manacher学习笔记+杂题
    字符串系列前言:孩子从小就自卑。四、最小表示法&Manacher学习笔记+杂题相关题单:戳我1.最小表示法最小表示法是用于解决字符串最小表示问题的方法。(1)字符串的最小表示:字符串\(s\)的最小表示为与\(s\)循环同构的所有字符串中字典序最小的字符串。循环同构指的是当字符......
  • C语言笔记9
       函数的参数传递形式参数:函数定义时的参数,简称形参。实际参数:函数调用时的参数,简称实参。实参与形参数目、类型和顺序应一致,占据不同存储单位。 理解单向值传递每个函数都有自己的变量空间,参数也位于这个空间;形参调用前不占内存单位,调用时对形参分配单位并传......