【图论】差分约束系统
前置芝士
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;
}
差分约束系统
给出一组包含 \(m\) 个不等式,有 \(n\) 个未知数的形如:
\[\begin{cases} x_{c_1}-x_{c'_1}\leq y_1 \\x_{c_2}-x_{c'_2} \leq y_2 \\ \cdots\\ x_{c_m} - x_{c'_m}\leq y_m\end{cases} \]的不等式组,求任意一组满足这个不等式组的解。
思考转化为图论
对于每个形如\(x1-x2\leq y\)的不等式,都可以转化为\(x1\leq x2+y\)
看着这个式子就很眼熟的样子
if(dis[to]>dis[k]+e[i].val)
{
dis[to]=dis[k]+e[i].val;
}
这不就是单源最短路径??
所以对于每个式子\(x1-x2\leq y\),都可以看做x2到x1有一条权值为y的连边,那么每个x对于源点0的最短路径长度dis[x]一定是满足不等式的一组解,而当有负环存在时,一定无解
其实还是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;
}
补充&总结
对于一些题目,其约束条件可能会形如\(x1-x2\geq y\),我们的解决方案通常是两边同时乘上-1,得到\(x2-x1\leq -y\),然后就和上面一样力,而当出现x1-x2=y时,我们可以拆分将其为\(x1-x2\leq y\)和\(x1-x2\geq y\)两个约束条件进行维护即可
To 初赛失利のOIer
标签:ch,int,笔记,约束,vis,差分,x2,include,dis From: https://www.cnblogs.com/SitoASK/p/16707656.html尘埃纵然很轻,但是尘埃永远领略不到云彩有多轻。因为云彩在尘埃遥不可及的平流层之上,俯瞰着一切,而尘埃也至多只能看见云彩投在地下的一段阴影。
但是呢,话也不能这么说。云彩随着大气环流不断更新,这一朵可能再不会与我这种尘埃相遇。所谓的阴影也只是一瞬罢了。
尘埃…也应该在地壳表面拥有自己的精彩啊。