差分约束法

差分约束系统

首先,我们先介绍一下什么是差分约束系统。

定义:如果一个系统由 n n n 个变量和 m m m 个约束条件组成,形成 m m m 个形如 a i − a j ≤ k a_i-a_j \leq k aiajk的不等式 。 i , j ∈ [ 1 , n ] i,j\in \left [ 1,n \right ] i,j[1,n] k k k为常数。

不绝对,若为 a i − a j ≥ k a_i-a_j \geq k aiajk,即是最长路)

可以理解为:差分约束是求解关于一组变量的特殊不等式组的方法。(即: 线性规划问题

举个例子:

在样例中,如果我们把不等式等价成矩阵的形式,即:
[ 1 − 1 0 0 1 − 1 1 0 − 1 ] [ x 1 x 2 x 3 ] ≤ [ 3 − 2 1 ] \begin{bmatrix} 1& -1& 0 \\ 0& 1& -1 \\ 1& 0& -1 \end{bmatrix} \begin{bmatrix} x_1\\ x_2\\ x_3 \end{bmatrix} \leq \begin{bmatrix} 3\\ -2\\ 1 \end{bmatrix} 101110011 x1x2x3 321
关于上面这个不等式的解,可以是 x = ( 5 , 3 , 5 ) x=\left( 5,3,5\right) x=(5,3,5),也可以是 x = ( 0 , − 2 , 0 ) x=\left( 0,-2,0\right) x=(0,2,0)

观察两个解,不难看出:两解相差一个定值 5。

所以,我们可以得出一个结论:

x = ( x 1 , x 2 , x 3 , . . . , x n ) x=\left( x_1,x_2,x_3,...,x_n\right) x=(x1,x2,x3,...,xn)是不等式的一个解。设 d d d为任意常数,则 x + d = ( x 1 + d , x 2 + d , x 3 + d , . . . , x n + d ) x+d=\left( x_1+d,x_2+d,x_3+d,...,x_n+d\right) x+d=(x1+d,x2+d,x3+d,...,xn+d)也是该不等式的一个解。

说话讲道理,证明:

对于每一个 x i , x j x_i,x_j xi,xj,我们有 ( x i + d ) − ( x j + d ) = x i − x j \left(x_i+d\right)-\left(x_j+d\right)=x_i-x_j (xi+d)(xj+d)=xixj。因此若 x x x满足不等式, x + d x+d x+d也满足。

(这也是这一题special judge的原因)

约束图

刚刚分析了差分约束系统。那这一问题和图论又有什么关系呢?

敏感的小伙伴在看到上面的矩阵时,应该就反应过来了。

这是一个有向无环图的关联矩阵

(即: 从减数指向被减数

再将边的权重赋值为不等式的常数 k k k

最后构造一个点(起点),从其出发可以到达所有其他节点,权值赋为0。

即构成了差分约束系统的约束图。

求解

对于约束图,若图中不存在负环。则起点到每个边的最短路权值,即为差分约束系统的一个可行解。

若存在负环,则无解。

代码如下(SPFA判负环)

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MAXN 500000
using namespace std;
int adj[MAXN],cnt=0,num[MAXN];
int dis[MAXN],ori=0,n,m;;
bool vis[MAXN];
queue < int > q;
struct EDGE
{
	int to,nxt,val;
}	e[MAXN];
void addedge(int u,int v,int w)
{
	e[++cnt].to=v; e[cnt].val=w; e[cnt].nxt=adj[u]; adj[u]=cnt;
}
bool SPFA()
{
	for(int i=1;i<=n;++i) dis[i]=(i==ori ? 0 : INF);
	q.push(ori); vis[ori]=1; ++num[ori];
	while(!q.empty())
	{
		int u=q.front(); q.pop(); vis[u]=0;
		for(int i=adj[u];i;i=e[i].nxt) 
		{
			int v=e[i].to;
			if(dis[v]>dis[u]+e[i].val) 
			{
				dis[v]=dis[u]+e[i].val;
				if(!vis[v])
				{
					vis[v]=1;q.push(v);
					++num[v];
					if(num[v]>n)	return 0;
				}

			}
		}
	}
	return 1;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) addedge(0,i,0);
	for(int i=1;i<=m;++i)
	{
		int u,v,w;	scanf("%d%d%d",&u,&v,&w);
		addedge(v,u,w);
	}
	if(SPFA())
		for(int i=1;i<=n;++i)	printf("%d ",dis[i]);
	else
		printf("NO");
	return 0;
}

补充:

差分约束在实际应用中很丰富。可以是最短路径,也可以是最长路径。

在实际问题中,要尽可能的把题目中个不等关系找全。(如: 0 ≤ x 2 − x 1 ≤ 1 0 \leq x_2-x_1\leq 1 0x2x11,前半个容易丢)

参考资料:《算法导论》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值