差分约束系统(zoj1508)

差分约束系统(zoj1508)

给出一组限制条件 a[i] - a[j] ≥ k 或 a[i] - a[j] ≤ k。
要你求出a[t] - a[s]的最小值或最大值。

先举一个题目的例子:poj1201.
题目大意是,有一个集合S。已知其满足以下一些条件:
对于给出的n组 a[i] b[i] c[i],有从a[i]~b[i]这连续的k个整数中,至少有c[i]个在集合S内。求S最少的元素个数。

这个题目转化为我们的差分约束系统如下:
如果i∈S,则t[i]=1,否则t[i]=0。另s[i] = ∑t[j] (j = 0 ~ i),这样子,题目的条件就可以用下面的式子表示:
s[b[i]] - s[a[i]-1] ≥ c[i]
s[i] - s[i+1] ≥ -1
s[i+1] - s[i] ≥ 0
注意后面两个式子是s先天的性质。
我们要求的就是s[n] - s[-1]的最小值(因为题目都是非负的嘛)。

然后我们介绍解决差分约束问题的方法:Bellman-Ford算法,是不是很神奇呢?没错,差分约束系统可以通过转换成图论最短路问题来解决:
注意到最短路算法的松弛操作:if (d[j] > d[i] + w[i][j]) d[j] = d[i] + w[i][j]。
这其中的三角形不等式:d[j] ≤ d[i] + w[i][j]简单变形就成了d[i] - d[j] >= -w[i][j]。这样就图形的最短路就维护了一个不等式组。所以,我们可以建立一个图:对于每一个不等式s[i] - s[j] >= c,就从j连一条指向i的边,其中边的权值c,这样求一个最长路,就是d[n] - d[-1]就是s[n] - s[-1]的最小值了,且对应的方案就是s[i] = d[i]。

有的同学要问了:无解怎么办啊?很简单,你将会发现Bellman-Ford算法如果找出了”负权回路”,那么该系统无解。只要系统无解,就必然存在”负权圈”。
那么如果求s[n] - s[-1]的最小值呢?其实和上面的方法类似了,大家可以自己推导一下。而且有很多问题仅仅要你给出是否有解的判断,那就不要想那么多了。

实际上是求最长路,其实就是把松弛操作的符号改变即可

其实此题可以用spfa实现,效率很高

开始做此题时

连续超时很久

此题每次要进行三次松弛操作

 1  for ( int  j = Min;j < Max; ++ j)
 2  {
 3         if (d[j] != INT_MIN && d[j] > d[j + 1 ])
 4        {
 5              d[j + 1 ] = d[j];
 6              update = 0 ;
 7        }
 8  }
 9  for ( int  j = Max;j > Min; -- j)
10  {
11         if (d[j] != INT_MIN && d[j] - 1 > d[j - 1 ])
12        {
13              update = 0 ;
14              d[j - 1 ] = d[j] - 1 ;
15        }
16  }
一开始将两个松弛操作合并到一起进行

超时

第二次分开

仍然超时

第三次

改变松弛的顺序

即第三个松弛从上到下(之前是从下到上)

这样做的目的就是避免了重复的松弛操作!

 1  #include  < iostream >
 2  using   namespace  std;
 3  struct  Edge
 4  {
 5         int  x,y,d;
 6  }edge[ 50000 + 5 ];
 7  int  n;
 8  int  d[ 50000 + 5 ];
 9  int  main()
10  {      
11        while (scanf( " %d " , & n) != EOF)
12       {
13               int  Min = INT_MAX;
14               int  Max = INT_MIN;
15               for ( int  i = 0 ;i < n; ++ i)
16              {
17                    scanf( " %d%d%d " , & edge[i].x, & edge[i].y, & edge[i].d);
18                     if ( -- edge[i].x < Min)Min = edge[i].x;
19                     if (edge[i].y > Max)Max = edge[i].y;
20              }
21               int  tmp = Max - Min + 1 ;
22               for ( int  i = Min;i <= Max; ++ i)
23                    d[i] = INT_MIN;
24              d[Min] = 0 ;
25               for ( int  i = 0 ;i < tmp; ++ i)
26              {
27                     bool  update = 1 ;
28                     for ( int  j = 0 ;j < n; ++ j)
29                    {
30                           if (d[edge[j].x] != INT_MIN && d[edge[j].y] < d[edge[j].x] + edge[j].d) 
31                          {
32                                update = 0 ;
33                                d[edge[j].y] = d[edge[j].x] + edge[j].d;
34                          }
35                    }
36                     for ( int  j = Min;j < Max; ++ j)
37                    {
38                           if (d[j] != INT_MIN && d[j] > d[j + 1 ])
39                          {
40                                d[j + 1 ] = d[j];
41                                update = 0 ;
42                          }
43                      }
44                     for ( int  j = Max;j > Min; -- j)
45                    {
46                           if (d[j] != INT_MIN && d[j] - 1 > d[j - 1 ])
47                          {
48                                update = 0 ;
49                                d[j - 1 ] = d[j] - 1 ;
50                          }
51                    }
52                     if (update) break ;
53              }
54              printf( " %d\n " ,d[Max] - d[Min]);
55        }
56         return   0 ;
57  }

你可能感兴趣的:(差分约束系统(zoj1508))