zoukankan      html  css  js  c++  java
  • 差分约束系统(转)

    1.问题定义

    差分约束系统属于线性规划问题。在一个差分约束系统中,线性规划矩阵A的每一行包含一个1和一个-1,A的所有其他元素都为0。因此,由Ax≤b给出的约束条件是m个差分约束集合,其中包含n个未知元。每个约束条件为如下形式的简单线性不等式:xj-xi≤bk(1≤i, j≤n,1≤k≤m)。如下图5维向量x满足8个不等式的差分约束,我们可以把未知量x1,x2,x3,x4,x5化为如下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=(x1,x2,…,xn)是差分约束系统Ax≤b的一个解,d为任意常数。则x+d=(x1+d,x2+d,…,xn+d)也是该系统Ax≤b的一个解。因此这个不等式组要么无解,要么就有无数组解。

     

    2.解决方案

    求解差分约束系统,可以转化成图论的单源最短路径问题。观察xj-xi<=bk,会发现它类似最短路中的三角不等式d[v] <= d[u] + w[u,v],即d[v] - d[u] <= w[u,v]。因此,以每个变量xi为结点,对于约束条件xj-xi<=bk,连接一条边E(i,j),边权为bk。求单源最短路径,必须有一个源点,然后再求这个源点到其他所有点的最短路径,我们可以增加一个原点s与所有其他点相连,边权均为0,xi - x0 <= 0。上图中的不等式于是转化为下图中的有向图:

    下面介绍解决差分约束问题的方法:Bellman-Ford算法。Bellman-Ford算法是用来解决有向图中存在负权边的单源最短路径问题的。

    ①将除源点外的所有顶点的最短距离估计值,即 d[v] = +∞, d[s] = 0;

    ②反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;

    ③判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,问题有解,并且从源点可到达的顶点v的最短距离保存在d[v]中。

    算法伪代码如下:

    Bellman-Ford()
    {
       for each vertex v ∈ G do             //初始化
    
             d[v] = +∞
    
       d[s] = 0
    
      
    
       for i = 1 to n-1 do
    
          for each edge(u, v) ∈ G do
    
             if d[v] > d[u] + w(u, v) then   //松弛操作
    
                d[v] = d[u] + w(u, v)
    
     
    
       for each edge(u, v) ∈ G do
    
           if d[v] > d[u] + w(u, v) then    //检查是否存在回路
    
              return false
    
       return true
    
     }

    图中任意一条最短路径既不能包含负权回路,也不会包含正权回路,所以最多包含n-1条边,从源点s出发每进行一遍松弛操作时,多生成了了从s出发层次为1的树,而最短边路径最多为n-1,故只需要循环n-1次。最后计算的d[v]即为差分不等式的一组解。

    3.算法优化

    Bellman-Ford算法的时间复杂度为O(VE),一种方法是我们引入SPFA算法作为其优化算法,也就是进行队列优化,初始化时将源s加入队列每次从队列中取出一个元素,并且对所有与之相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队,直到队列为空时SPFA算法结束。SFPA的期望时间复杂度为O(KE)[己考证,证明不严密,最坏情况和Dijkstra差不多],期望的常数K一般不会超过2。另一种优化是Yen优化(不太懂),将图中的点随机的进行标记序号v1,v2,vi...,则分为两类边集合:                                

    差分约束系统

    进行松驰时,先按vi的升序只找Ef边进行松弛,然后再按vi的降序只对Eb边进行松弛,可以优化为常数时间,复杂度仍为O(VE)[]。

    4.题目 POJ1364 King

    题目大意:
    *        已知一个序列a[1], a[2], ......, a[n],给出它的若干子序列以及对该子序列的
    *        约束条件,例如a[si], a[si+1], a[si+2], ......, a[si+ni],且a[si]+a[si+1]
    *        +a[si+2]+......+a[si+ni] < or > ki。求是否存在满足以上m个要求的数列。是
    *        则输出“lamentable kingdom”,否则输出“successful conspiracy”。
    解题思路:
    *        s[a] + s[a+1] + …… + s[b] < c 可以转化成前n项和sum[b] - sum[a - 1] < c,
    *        为了能用Bellman_Ford,即将< 转化成 <= ,sum[b] - sum[a - 1] <= c - 1。
    *        转化为最长路或者最短路来判断是否有解都可以。

    #include <cstdio>
    #include <iostream>
    #include <memory.h>
    #include <string>
    #include <climits>
    
    using namespace std;
    
    
    struct Line
    {
        int from;
        int to;
        int value;
    }line[502];
    
    
    int d[102];
    
    int main(int argc, char** argv)
    {
        int n, m;
        int si,ni,ki;
        string oi;
        
        //freopen("E:/sample_input.txt", "r", stdin);
    
        while (scanf("%d", &n), n)  
        {
            scanf("%d", &m);
            int num = 1;
            for(int i=0; i<=n; ++i)
            {
                line[num].from = n+1;
                line[num].to = i;
                line[num].value = 0;
                num++;
            }
            for(int i=1; i<=m; i++)
            {
                cin >> si >> ni >> oi >> ki;
    
                if(oi == "gt")
                {
                    line[num].from = si+ni;
                    line[num].to = si-1;
                    line[num].value = -ki-1;
                    num++;
                }
                else
                {
                    line[num].from = si-1;
                    line[num].to = si+ni;
                    line[num].value = ki-1;
                    num++;
                }
    
            }
    
    
            //bellman-ford
            for(int i=0; i<=n; ++i)
            {
                d[i] = INT_MAX;
            }
            d[n+1] = 0;
            bool flag;
            for(int i=0; i<=n; ++i)
            {
                flag = true;
    
                for(int j=1; j<num; ++j)
                {
                    if(d[line[j].to] > d[line[j].from] + line[j].value)
                    {
                        d[line[j].to] = d[line[j].from] + line[j].value;
                        flag = false;
                    }
                }
                if(flag)
                    break;
            }
    
            flag = true;
            for(int j=1; j<num; ++j)
            {
                    if(d[line[j].to] > d[line[j].from] + line[j].value)
                    {
                        flag = false;
                    }
            }
    
            if(flag)
            {
                cout << "lamentable kingdom" << endl;
            }
            else
            {
                cout << "successful conspiracy" << endl;
            }
    
        }
    
    
        return 0;
    }
  • 相关阅读:
    存储过程中的top+变量
    SQL Server中Table型数据(表变量)与用户自定义函数
    在IE中调用javascript打开Excel
    微软公司软件开发模式简介收集
    一个相当独立的.通用分页控件c#源码一(downmoon收集)
    导出GridView到Excel中的关键之处(downmoon)
    一个相当独立的.通用分页控件c#源码二(downmoon收集)
    三个很常用的存储过程
    前触发器和后触发器简介
    .net2.0中新增的Substitution控件动态更新缓存页的部分(也可用于局部刷新)
  • 原文地址:https://www.cnblogs.com/scarecrow-blog/p/4548473.html
Copyright © 2011-2022 走看看