差分约束学习笔记

1有n个未知数,其中有m个约束条件,形如x1-x2<=c之类的,问是否存在满足所有约束条件的情况。

2对于条件x1-x2<=c,可以转换成x1<=c+x2,此时可以建一个x2连向x1边权为c的边,因为这个公式和最短路中的松弛操作很像。假设有两个式子,x1<=c+x2和x1<=c+x3,分别建一条x2到x1和x3到x1的边权为c的边,为了满足条件,x1肯定越小越好,如果x1出度为0,那么x1怎么小都没影响,而x1有指向其他点的边,那么x1尽量要大一点,最短路求出的就是其最大值。

3创造一个超级源点,连接所有的点,边权为0,跑spfa判断是否有负环,如果有负环,说明不满足约束条件

代码实现:

根据模板题:小 K 的农场 - 洛谷

1数据存储:

const int maxn=1e4+10;
const int inf=0x3f3f3f3f;
int n,m;
struct edge{
	int to,w;
};
vectorvv[maxn];
void add(int u,int v,int w)
{
	vv[u].push_back({v,w});
}
bool in[maxn];//标记是否入队列 
int cnt[maxn];//spfa记录到达该点经过的路径 
int dis[maxn];//最短路 

2spfa判断负环

bool spfa(int s)
{
	for(int i=1;i<=n;i++)
	dis[i]=inf;
	dis[s]=0;
	queueq;
	q.push(s);
	in[s]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		in[u]=0;
		for(int i=0;idis[u]+w)
			{
				dis[to]=dis[u]+w;
				cnt[to]=cnt[u]+1;
				if(cnt[to]>=n+1)
				return 0;
				if(!in[to])
				{
					q.push(to);
					in[to]=1;
				}
			}
		}
	}
	return 1;
}

3主函数(建边)

int main()
{	
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int op,u,v,c;
		cin>>op;
		if(op==1)//u-c>=v   建一条u到v边权为-c的边 
		{
		cin>>u>>v>>c;
		add(u,v,-c);	
		}
		else if(op==2)//u<=v+c   建一条v到u权为c的边 
		{
			cin>>u>>v>>c;
			add(v,u,c);
		}
		else//u<=v+0  u+0>=v    分别建一条u到v和v到u边权都为0的边 
		{
			cin>>u>>v;
			add(u,v,0);
			add(v,u,0);
		}
	}
	for(int i=1;i<=n;i++)
	add(0,i,0);
	if(spfa(0))
	cout<<"Yes\n";
	else
	cout<<"No\n";
	return 0;
}

4完整代码

#include
#define ll long long
using namespace std;
const int maxn=1e4+10;
const int inf=0x3f3f3f3f;
int n,m;
struct edge{
	int to,w;
};
vectorvv[maxn];
void add(int u,int v,int w)
{
	vv[u].push_back({v,w});
}
bool in[maxn];//标记是否入队列 
int cnt[maxn];//spfa记录到达该点经过的路径 
int dis[maxn];//最短路 
bool spfa(int s)
{
	for(int i=1;i<=n;i++)
	dis[i]=inf;
	dis[s]=0;
	queueq;
	q.push(s);
	in[s]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		in[u]=0;
		for(int i=0;idis[u]+w)
			{
				dis[to]=dis[u]+w;
				cnt[to]=cnt[u]+1;
				if(cnt[to]>=n+1)
				return 0;
				if(!in[to])
				{
					q.push(to);
					in[to]=1;
				}
			}
		}
	}
	return 1;
}
int main()
{	
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int op,u,v,c;
		cin>>op;
		if(op==1)//u-c>=v   建一条u到v边权为-c的边 
		{
		cin>>u>>v>>c;
		add(u,v,-c);	
		}
		else if(op==2)//u<=v+c   建一条v到u权为c的边 
		{
			cin>>u>>v>>c;
			add(v,u,c);
		}
		else//u<=v+0  u+0>=v    分别建一条u到v和v到u边权都为0的边 
		{
			cin>>u>>v;
			add(u,v,0);
			add(v,u,0);
		}
	}
	for(int i=1;i<=n;i++)
	add(0,i,0);
	if(spfa(0))
	cout<<"Yes\n";
	else
	cout<<"No\n";
	return 0;
}

你可能感兴趣的:(acm算法学习笔记,图论专题笔记,差分约束,spfa,c++,图论)