差分约束系统(入门)

21 篇文章 0 订阅
10 篇文章 0 订阅

简介:
如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则其为差分约束系统(system of difference constraints)。亦即,差分约束系统是关于一组变量的特殊不等式组。求解差分约束系统,可以转化成图论的单源最短路径问题。
观察xj-xi<=bk,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]。因此,以每个变量xi为结点,对于约束条件xj-xi<=bk,连接一条边(i,j),边权为bk。我们再增加一个源点s,s与所有点相连,边权均为0。对这个图,以s为源点运行SPFA算法,最终{d[i]}即为一组可行解。(差分约束系统的解的一个特点是,当将所有变量同时增加相同的大小,约束条件依然成立)
如果只是判断差分约束系统是否有解,则可将所有结点放入队列进行一次SPFA解决,即判断负环。

解法:
差分约束系统一般可以转化成图论的最短路或者最长路问题来解决,一般可以使用dijkstra算法或者spfa算法解决。因为dijkstra算法要求所有的边权为正,所以这里重点介绍spfa算法。

SPFA算法:
SPFA算法是bellman-ford算法的队列优化,减少了冗余的计算。
算法思路:实现与广搜类似,不同的是SPFA的结点可以多次进入
1. 采用动态逼近法,先建立一个FIFO队列来保存待优化的
2. 优化时每次取出队首结点 u ,并用 u 当前的最短路径对存在 u-v 路径的结点 v 进行,如果 v 的最短路径有所调整,而且 v 不在当前队列中,将 v 放入队尾
3. 重复 2,直到队列为(其中判负环的方法可以从点进入队列的次数来看,如果结点 u 进入队列超过 n 次,则存在负

时间复杂度: O(k*e) k一般在2左右 ,但是SPFA不太稳定,所以没有负权值一般还是用dijkstra

SPFA算法有两个优化算法 SLF 和 LLL:
SLF: Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j) < dist(i),则将j插入队首, 否则插入队尾;
LLL: Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i) > x则将 i 插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。

例题: POJ 3159
这题就是将差分约束系统转成最短路来做,而且不需要建立超级源点。这题不可以使用vector实现邻接表,需要自己实现前向星;而且不可以使用队列优化,得使用栈优化,否则会超时。

代码:

#include <stack>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;

int n,m,len,d[30010],inque[30010],cnt[30010],head[30010];
struct edge{
    int v,w,next;
}e[350010];

inline void addEdge(int u,int v,int w){
    e[len].v=v,e[len].w=w,e[len].next=head[u];
    head[u]=len++;
}

bool spfa()
{
    memset(cnt,0,sizeof(cnt));
    memset(d,INF,sizeof(d));
    stack<int> s;
    d[1]=0,inque[1]=1,cnt[1]=1,s.push(1);
    while(!s.empty())
    {
        int tmp=s.top();
        s.pop(),inque[tmp]=0;
        for(int i=head[tmp];i!=-1;i=e[i].next)
        {
            int index=e[i].v,w=e[i].w;
            if(d[index]>d[tmp]+w)
            {
                d[index]=d[tmp]+w;
                if(!inque[index])
                {
                    inque[index]=1,s.push(index),cnt[index]++;
                    if(cnt[index]>n)
                        return 0;
                }
            }           
        }
    }
    return 1;
}

int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        len=0;
        memset(head,-1,sizeof(head));

        int x,y,w;
        while(m--)
        {
            scanf("%d %d %d",&x,&y,&w);
            addEdge(x,y,w);
        }

        spfa();
        printf("%d\n",d[n]);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值