【学习笔记】差分约束系统
【图论】差分约束系统
前置芝士
SPFA判负环与最短路
SPFA判负环
负环定义:边の权值之和为负数的环
不会真的有人不会SPFA吧
先放张图
就是在SPFA跑最短路的时候判断一下有没有一个点被访问的次数大于n,因为对于任意一个1能够到达的却不存在负环的路径,这条路径上每个节点被访问的次数一定是1~n,否则其中一定会有负环存在(可以自己举例验证一下)
板子 AC代码
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=3e3+5;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int n,m,t;
int tot;
int cnt[maxn];
int dis[maxn];
bool vis[maxn];
int head[maxn];
struct edge
{
int to;
int val;
int from;
int next;
}e[2*maxn];
void add(int x,int y,int z)
{
tot++;
e[tot].to=y;
e[tot].from=x;
e[tot].val=z;
e[tot].next=head[x];
head[x]=tot;
}
bool spfa()
{
queue <int> q;
q.push(1);
dis[1]=0;
vis[1]=true;
cnt[1]=1;
while(!q.empty())
{
int k=q.front();
q.pop();
vis[k]=false;
for(int i=head[k];i;i=e[i].next)
{
int to=e[i].to;
if(dis[to]>dis[k]+e[i].val)
{
dis[to]=dis[k]+e[i].val;
if(vis[to]==false)
{
vis[to]=true;
q.push(to);
cnt[to]++;
if(cnt[to]>n)
{
return true;
}
}
}
}
}
return false;
}
signed main()
{
t=read();
while(t--)
{
tot=0;
memset(dis,0x3f3f3f,sizeof(dis));
memset(cnt,0,sizeof(cnt));
memset(vis,false,sizeof(vis));
memset(e,0,sizeof(e));
memset(head,0,sizeof(head));
n=read();
m=read();
for(int i=1;i<=m;i++)
{
int a=read();
int b=read();
int c=read();
add(a,b,c);
if(c>=0)
{
add(b,a,c);
}
}
if(spfa())
{
cout<<"YES"<<endl;
}
else
{
cout<<"NO"<<endl;
}
}
return 0;
}
差分约束系统
给出一组包含
的不等式组,求任意一组满足这个不等式组的解。
思考转化为图论
对于每个形如
看着这个式子就很眼熟的样子
if(dis[to]>dis[k]+e[i].val)
{
dis[to]=dis[k]+e[i].val;
}
这不就是单源最短路径??
所以对于每个式子
其实还是SPFA跑0到各个点的单源最短路径在加上判负环啦,就是个SPFA的板子
板子代码
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=5e3+5;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int tot;
int n,m;
int dis[maxn];
int head[maxn];
bool vis[maxn];
int cnt[maxn];
struct edge
{
int from;
int to;
int val;
int next;
}e[maxn*2];
void add(int x,int y,int z)
{
tot++;
e[tot].to=y;
e[tot].from=x;
e[tot].val=z;
e[tot].next=head[x];
head[x]=tot;
}
bool spfa()
{
queue <int> q;
memset(dis,0x3f3f3f,sizeof(dis));
memset(vis,false,sizeof(vis));
q.push(0);
dis[0]=0;
cnt[0]=1;
vis[0]=true;
while(!q.empty())
{
int k=q.front();
q.pop();
vis[k]=false;
for(int i=head[k];i;i=e[i].next)
{
int to=e[i].to;
if(dis[to]>dis[k]+e[i].val)
{
dis[to]=dis[k]+e[i].val;
if(vis[to]==false)
{
vis[to]=true;
cnt[to]++;
q.push(to);
if(cnt[to]>n+1)
{
return true;
}
}
}
}
}
return false;
}
int main()
{
n=read();
m=read();
for(int i=1;i<=m;i++)
{
int u,v,w;
u=read();
v=read();
w=read();
add(v,u,w);
}
for(int i=1;i<=n;i++)
{
add(0,i,0);//要记得把0和各个点加进去,不然0就是一个单独的节点了
}
if(!spfa())
{
for(int i=1;i<=n;i++)
{
cout<<dis[i]<<" ";
}
}
else
{
cout<<"NO"<<endl;
}
return 0;
}
推几道水题
解析:套板子即可
AC 代码
点击查看代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<climits>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=5010;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int n,m;
int tot;
int cnt[maxn];
int dis[maxn];
bool vis[maxn];
int head[maxn];
struct edge
{
int val;
int to;
int from;
int next;
}e[2*maxn];
void add(int x,int y,int z)
{
tot++;
e[tot].to=y;
e[tot].from=x;
e[tot].val=z;
e[tot].next=head[x];
head[x]=tot;
}
bool spfa()
{
queue <int> q;
memset(dis,0x3f3f3f,sizeof(dis));
q.push(0);
vis[0]=true;
cnt[0]++;
dis[0]=0;
while(!q.empty())
{
int k=q.front();
q.pop();
vis[k]=false;
for(int i=head[k];i;i=e[i].next)
{
int to=e[i].to;
if(dis[to]>dis[k]+e[i].val)
{
dis[to]=dis[k]+e[i].val;
if(vis[to]==false)
{
vis[to]=true;
cnt[to]++;
q.push(to);
if(cnt[to]>n+1)
{
return true;
}
}
}
}
}
return false;
}
int main()
{
n=read();
m=read();
for(int i=1;i<=m;i++)
{
int u=read();
int v=read();
int w=read();
add(v,u,w);
}
for(int i=1;i<=n;i++)
{
add(0,i,0);
}
int minn=INT_MAX;
if(spfa())
{
cout<<"NO SOLUTION";
}
else
{
for(int i=1;i<=n;i++)
{
minn=min(minn,dis[i]);
}
for(int i=1;i<=n;i++)
{
cout<<dis[i]-minn<<endl;
}
}
return 0;
}
解析:也是板子。。。水的一批
AC 代码
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=5e3+10;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int head[maxn];
int tot,n,m;
int cnt[maxn];
int dis[maxn];
bool vis[maxn];
struct edge
{
int from;
int to;
int val;
int next;
}e[maxn<<4];
void add(int x,int y,int z)
{
tot++;
e[tot].to=y;
e[tot].from=x;
e[tot].val=z;
e[tot].next=head[x];
head[x]=tot;
}
bool spfa()
{
queue <int> q;
memset(dis,0x3f3f3f,sizeof(dis));
q.push(0);
vis[0]=true;
dis[0]=0;
cnt[0]++;
while(!q.empty())
{
int k=q.front();
q.pop();
vis[k]=false;
for(int i=head[k];i;i=e[i].next)
{
int to=e[i].to;
if(dis[to]>dis[k]+e[i].val)
{
dis[to]=dis[k]+e[i].val;
if(vis[to]==false)
{
vis[to]=true;
q.push(to);
cnt[to]++;
if(cnt[to]>n+1)
{
return true;
}
}
}
}
}
return false;
}
int main()
{
n=read();
m=read();
for(int i=1;i<=m;i++)
{
int opt=read();
if(opt==1)
{
int u=read();
int v=read();
int w=read();
add(u,v,-w);
}
if(opt==2)
{
int u=read();
int v=read();
int w=read();
add(v,u,w);
}
if(opt==3)
{
int u=read();
int v=read();
add(u,v,0);
add(v,u,0);
}
}
for(int i=1;i<=n;i++)
{
add(0,i,0);
}
if(spfa())
{
cout<<"No";
}
else
{
cout<<"Yes";
}
return 0;
}
补充&总结
对于一些题目,其约束条件可能会形如
To 初赛失利のOIer
尘埃纵然很轻,但是尘埃永远领略不到云彩有多轻。因为云彩在尘埃遥不可及的平流层之上,俯瞰着一切,而尘埃也至多只能看见云彩投在地下的一段阴影。
但是呢,话也不能这么说。云彩随着大气环流不断更新,这一朵可能再不会与我这种尘埃相遇。所谓的阴影也只是一瞬罢了。
尘埃…也应该在地壳表面拥有自己的精彩啊。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现