差分约束系统小结

 

图论终于写完啦!

居然写了快两个小时才写出这最后一个板子=-=

那么这只蒟蒻就随便写一下我的差分约束的理解

如果发现写的哪里有问题请私信作者并且骂我

照例先上定义 转自http://blog.csdn.net/xuezhongfenfei/article/details/8685313

X1 - X2 <= 0
X1 - X5 <= -1
X2 - X5 <= 1
X3 - X1 <= 5
X4 - X1 <= 4
X4 - X3 <= -1
X5 - X3 <= -3
X5 - X4 <= -3
不等式组(1) 
    全都是两个未知数的差小于等于某个常数(大于等于也可以,因为左右乘以-1就可以化成小于等于)。这样的不等式组就称作差分约束系统。
    这个不等式组要么无解,要么就有无数组解。因为如果有一组解{X1, X2, ..., Xn}的话,那么对于任何一个常数k,{X1 + k, X2 + k, ..., Xn + k}肯定也是一组解,因为任何两个数同时加一个数之后,它们的差是不变的,那么这个差分约束系统中的所有不等式都不会被破坏。  
    差分约束系统的解法利用到了单源最短路径问题中的三角形不等式。即对于任何一条边u -> v,都有:
d(v) <= d(u) + w(u, v) 
    其中d(u)和d(v)是从源点分别到点u和点v的最短路径的权值,w(u, v)是边u -> v的权值。
    显然以上不等式就是d(v) - d(u) <= w(u, v)。这个形式正好和差分约束系统中的不等式形式相同。于是我们就可以把一个差分约束系统转化成一张图,每个未知数Xi对应图中的一个顶点Vi,把所有不等式都化成图中的一条边。对于不等式Xi - Xj <= c,把它化成三角形不等式:Xi <= Xj + c,就可以化成边Vj -> Vi,权值为c。最后,我们在这张图上求一次单源最短路径,这些三角形不等式就会全部都满足了,因为它是最短路径问题的基本性质嘛。
    话说回来,所谓单源最短路径,当然要有一个源点,然后再求这个源点到其他所有点的最短路径。那么源点在哪呢?我们不妨自已造一个。以上面的不等式组为例,我们就再新加一个未知数X0。然后对原来的每个未知数都对X0随便加一个不等式(这个不等式当然也要和其它不等式形式相同,即两个未知数的差小于等于某个常数)。我们索性就全都写成Xn - X0 <= 0,于是这个差分约束系统中就多出了下列不等式: 
X1 - X0 <= 0
X2 - X0 <= 0
X3 - X0 <= 0
X4 - X0 <= 0
X5 - X0 <= 0
不等式组(2) 
    对于这5个不等式,也在图中建出相应的边。最后形成的图如下:
图1 

 图中的每一条边都代表差分约束系统中的一个不等式。现在以V0为源点,求单源最短路径。最终得到的V0到Vn的最短路径长度就是Xn的一个解啦。从图1中可以看到,这组解是{-5, -3, 0, -1, -4}。当然把每个数都加上10也是一组解:{5, 7, 10, 9, 6}。但是这组解只满足不等式组(1),也就是原先的差分约束系统;而不满足不等式组(2),也就是我们后来加上去的那些不等式。当然这是无关紧要的,因为X0本来就是个局外人,是我们后来加上去的,满不满足与X0有关的不等式我们并不在乎。

 如果没看懂或者太长不看也没关系(

下面口胡人话定义:

给定一系列用不等式表达的约束,让你求出一组解的最值

对于这个问题 有一种非常神的思路

假设有一个不等式 u<=v+1

像上面给的图一样 可以将它看成u和v之间连了一条权值为1的边

那么求解差分约束系统的过程 实质上就是对整个图 求一次单源最值路径的过程

这个源头是什么呢 一些题目会约定某些点的值是不能被修改的 但更多的时候我们需要自己来自己选一个虚点

很多材料会直接选用0 但是在一些题目里(比如下面的例题)会出现0点是有意义的情况 因此我还是推荐用n+1为原点

选定原点之后 从原点向各个点分别建一条权值为0的边,这样建图就算结束了

-----------------------------------------------------------------------------------

为什么前面不说最短路呢?实际上 求最大还是最短 是由我们的差分约束系统而决定的

当不等式组为<=时 相当于我们跑最短路

反过来 >=的时候 当然就是跑最长路了 这里我们选用spfa算法 因为在求解这个系统的时候,spfa可以保证求出来的是最大(小)的一组解

当然其实是要严谨的证明的 ,由于博主太弱就不提到了qwq

注意事项:

1.建立系统的时候 不等式组必须是带等于号的 不然会有很多奇怪的错误 因此形如x<i这样的约束,我们将它换成x<=i-1即可

2.尽量挑选需要建立较少负权边的系统建立,例如下面这道题,跑最短路需要比跑最长路多出一堆奇怪的操作,不仅调试困难,也很容易掉入思维陷阱

例题:

代码:

 

#pragma GCC optimize("O2")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<limits.h>
#include<ctime>
#define N 100001
typedef long long ll;
const int inf=0x3fffffff;
const int maxn=2017;
using namespace std;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch>'9'||ch<'0')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<3)+(x<<1)+ch-'0';
        ch=getchar();
    }
    return f*x;
}
struct tsdl{
	int w,to,next;
} edge[N*4];
int cnt[N],tot,head[N],inq[N],d[N];
void add(int ui,int vi,int wi)
{
	edge[++tot].next=head[ui];
	edge[tot].w=wi;
	edge[tot].to=vi; 
    head[ui]=tot;
}
queue<int>q;
bool spfa(int u,int n)
{
    memset(d,0xff,sizeof(d));
    d[u]=0,inq[u]=1;
    q.push(u);
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        inq[x]=0;
        for(int i=head[x];i!=-1;i=edge[i].next)
        {
        	int v=edge[i].to;
        	if(d[v]<d[x]+edge[i].w)
        	{
	        	d[v]=d[x]+edge[i].w;
	        	if(!inq[v])
	        	{
	        		inq[v]=1;
	        		q.push(v);
	        		if(++cnt[v]>n)return 1;
	        	}
	        }
		}
    }
    return 0;
}
int main()
{
	memset(head,-1,sizeof(head));
	int n=read(),h=read();
    for(int i=1;i<=h;i++)
    {
    	int u=read(),v=read(),w=read();
    	add(u-1,v,w);
   	}
    for(int i=1;i<=n;i++)
    {
    	add(i,i-1,-1);
    	add(i-1,i,0);
    }
	for(int i=0;i<n;i++)
    add(n+1,i,0);
	spfa(n+1,n);
	printf("%d",d[n]);
}

 

转载于:https://www.cnblogs.com/tsunderehome/p/7511318.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值