【Luogu 1993】差分约束系统问题——小K的农场
Luogu P1993
前置知识:最短路径相关算法
如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统。
显然题目中给出的信息可以构成一个差分约束系统,虽然不等号的方向不统一,但是我们可以利用不等式的性质进行变换,将其全部统一成小于等于号(更换成小于等于号的原因是需要用到三角不等式)。
众所周知,最短路径算法中有一条三角不等式,即:
\[cost[x]<=cost[y]+val[y][x]
\]
通过移项就可以变化成:
\[cost[x]-cost[y]<=val[y][x]
\]
这与题目中给出的条件形式上是完全一致的。
那么我们就找到了解差分约束系统的方法:把形如\(a-b<=c\)的不等式视为在一个有向图中从\(b\)节点连一条权值为\(c\)的边到\(a\)节点,通过单源最短路径算法就可以求解。
那么如果图不连通怎么办?当然是选择加入一个超级点,向每一个节点连接一条权值为0的边。(其实好像也可以对每一个连通块其中的一个点跑一次SPFA)
//类BFS的SPFA会TLE,开O2可过
//建议使用类DFS的SPFA,然而我并不会(判负环时DFS-SPFA比较高效)
#include<cstdio>
#include<queue>
using namespace std;
struct data
{
int to,next,val;
}e[50005];
int cost[50005],flag,head[50005],n,m,a,b,c,cnt,cnt1[50005];
queue<int> que;
bool vis[50005];
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].next=head[u];
e[cnt].val=w;
head[u]=cnt;
}
void First(int s)
{
for (int i=1;i<=n;i++) cost[i]=0x3f3f3f3f;
cost[s]=0;
}
bool SPFA(int s)
{
que.push(s);
vis[s]=true;
while (!que.empty())
{
int now=que.front();
que.pop();
vis[now]=false;
for (int i=head[now];i;i=e[i].next)
{
if (cost[e[i].to]>cost[now]+e[i].val)
{
cost[e[i].to]=cost[now]+e[i].val;
if (!vis[e[i].to])
{
que.push(e[i].to);
vis[e[i].to]=true;
}
cnt1[e[i].to]++;
if (cnt1[e[i].to]==n) return false;
}
}
}
return true;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&flag,&a,&b);
if (flag!=3) scanf("%d",&c);
if (flag==1) add(a,b,-c);
if (flag==2) add(b,a,c);
if (flag==3)
{
add(a,b,0);
add(b,a,0);
}
}
for (int i=1;i<=n;i++)
add(n+1,i,0);
First(n+1);
SPFA(n+1);
if (SPFA(n+1)) printf("Yes");
else printf("No");
return 0;
}