【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;
}
posted @ 2019-11-28 20:35  Nanjo  阅读(152)  评论(0编辑  收藏  举报