[置顶] 差分约束系统 讲解与例题分析

差分约束系统
 
一:基础知识
          如果一个系统由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为源点运行Bellman-ford算法(或SPFA算法),最终{d[ i]}即为一组可行解。
                         
         例如,考虑这样一个问题,寻找一个5维 向量 x=(xi)以满足:
         这一问题等价于找出未知量xi,i=1,2,…,5,满足下列8个差分约束条件:

  

x1-x2≤0
                       x1-x5≤-1
                       x2-x5≤1
                       x3-x1≤5
                       x4-x1≤4
                       x4-x3≤-1
                       x5-x3≤-3
                       x5-x4≤-3
        该问题的一个解为x=(-5,-3,0,-1,-4),另一个解y=(0,2,5,4,1),这2个解是有联系的:y中的每个元素比x中相应的元素大5。
        引理:设x=(x1,x2,…,xn)是差分约束系统Ax≤b的一个解,d为任意常数。则x+d=(x1+d,x2+d,…,xn+d)也是该系统Ax≤b的一个解。
        bellman-ford算法 伪代码
for each v V do d[v] <-- 无限大; d[s] <-- 0
Relaxation
for i =1,...,|V|-1 do
for each edge (u,v) 属于 E do
d[v] <-- min{d[v], d[u]+w(u,v)}
Negative cycle checking
for each v 属于V do if d[v]> d[u] + w(u,v) then no solution
<span style="font-family:Arial;BACKGROUND-COLOR: #ffffff"></span>
 
 
      在实际的应用中,一般使用 SPFA (Shortest Path Fast Algorithm)算法来实现。
      差分约束系统中源点到每个点的距离确定
      关于Dist[]的初始化化
            1.如果将源点到各点的距离初始化为0,最终求出的最短路满足 它们之间相互最接近了
            2.如果将源点到各点的距离初始化为INF(无穷大),其中之1为0,最终求出的最短路满足 它们与该点之间相互差值最大。
            3.差分约束系统的确立要根据自己确定的约束条件,从约束点走向被约束点
     连边一般有两种方法,第一种是连边后求最长路的方法,第二种是连边后求最短路的方法。
     例:d[x]-d[y]>=Z
           如果想连边后求最长路 那么将不等式变形为这种形式 d[x]>=d[y]+z y---x连一条权值为z的边
           求最短路则变形成d[y]<=d[x]-z x---y连一条权值为-z的边。
           如果是别的不等式,也可以根据情况变形。但是要保证的是 两个变量(x,y)的系数一定要是正的。而 常量 则不一定。

      参考资料:百度百科

      笔者经验:差分约束系统就是给定一组不等式组,然后对于每个不等式<x1-x2≤k>中x2到x1连一条有向边,权值为k,然后建立一个超级汇点<定为0>,那么0号节点到其他点的距离为0,注意负权边的spfa,若某一点入队超过N<即所有点数目>,则无解

 

二:例题<POJ1364>

King
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 9040   Accepted: 3395

Description

对题目中给定的si,ni,ki,和一个给定的序列S[1....N],如果格式为(si,ni,gt,ki),意思就是新增一约束条件S[si]+S[si+1]+...S[si+ni]>ki,如果格式为(si,ni,lt,ki),意思就是新增一约束条件S[si]+S[si+1]+...S[si+ni]<ki,判断所给的约束条件有符合要求的序列S[1....N],有解就输出lamentable kingdom,无解就输出successful conspiracy。

Sample Input

4 2
1 2 gt 0
2 2 lt 2
1 2
1 0 gt 0
1 0 lt 0
0

Sample Output

lamentable kingdom
successful conspiracy
 
【分析】:
    设a[i]表示∑a[k],k∈[1,i]且k∈Z*,那么对于约束条件S[si]+S[si+1]+...S[si+ni]>ki可转变为a[si+ni]-a[si-1]>ki(因为要包含[si,si+ni],因此根据a数组的定义,下限为a[si-1]),而差分约束系统只能解形如Ax≤b的不等式组,因此转化给出的约束条件S[si]+S[si+1]+...S[si+ni]>ki→a[si+ni]-a[si-1]>ki→a[si-1]-a[si+ni]<=-ki-1,另一个同理:a[si+ni]-a[si-1]<=ki-1,然后跑一遍差分约束即可<这里用的是spfa+邻接表存储>
【基础理论】:差分约束系统,用最短路算法实现
【代码】:
/*
   Problem: 1364  User: csyzcyj 
   Memory: 192K  Time: 0MS 
   Language: C++  Result: Accepted 
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
#define MAX 201
#define MAXP 1001
#define IMAX 21474836
struct EDGE{int to,next,v;};
EDGE a[MAXP];
int N,M,tot=0,last[MAX],dist[MAX],tot1[MAX];     
bool vis[MAX];
void add(int x,int y,int value)
{
      a[++tot].to=y;
      a[tot].next=last[x];
      a[tot].v=value;
      last[x]=tot;
}
bool check()
{
      queue<int> Q;
      memset(dist,0,sizeof(dist));
      for(int i=0;i<=N;i++)
            Q.push(i);
      while(!Q.empty())
      {
            int now=Q.front();
            Q.pop();
            vis[now]=false;
            for(int i=last[now];i!=-1;i=a[i].next)
            {
                  int now_to=a[i].to;
                  if(dist[now]+a[i].v<dist[now_to])
                  {
                        dist[now_to]=dist[now]+a[i].v;
                        if(!vis[now_to])
                        {
                              vis[now_to]=true;
                              tot1[now_to]++;
                              if(tot1[now_to]>N)   return false;   
                              Q.push(now_to);
                        }
                  } 
            }
      }
      return true;
}
int main()
{
      //freopen("input.in","r",stdin);
	  //freopen("output.out","w",stdout); 
	  while(scanf("%d%d",&N,&M)!=EOF && N!=0 && M!=0)
	  {
              memset(last,-1,sizeof(last));      
              memset(vis,true,sizeof(vis));
              memset(tot1,0,sizeof(tot1));
              memset(a,0,sizeof(a));
              tot=0;
              for(int i=1;i<=M;i++)
              {
                    int A,B,C;
                    char ch1,ch2;
                    scanf("%d%d",&A,&B);
                    scanf("%c%c%c",&ch1,&ch1,&ch2);
                    scanf("%d",&C);
                    if(ch1=='g')
                          add(A+B,A-1,-C-1);
                    if(ch1=='l')
                          add(A-1,A+B,C-1);
              }
              if(check())   printf("lamentable kingdom\n");
              else    printf("successful conspiracy\n");
      }
	  //system("pause");
      return 0;
}

 

转载注明出处:http://blog.csdn.net/u011400953

 

 

你可能感兴趣的:([置顶] 差分约束系统 讲解与例题分析)