码迷,mamicode.com
首页 > 其他好文 > 详细

ZOJ– 2770Burn the Linked Camp单源最短路径差分约束系统

时间:2015-06-11 20:57:31      阅读:145      评论:0      收藏:0      [点我收藏+]

标签:

ZOJ Problem Set – 2770 Burn the Linked Camp

题目的大意为:

陆逊侦查得知刘备将自己的军队分为N个营,从左到右编号为1,2,3…N。第i个营有最大士兵数Ci,通过一段时间的观察,陆逊可以估算至少有k个士兵住在第i到第j个营地,最后陆逊必须要估算刘备的军队至少有多少士兵,以便应对。

输入:

有多个测试案例,在每个测试案例的第一行,有两个整数N(0<n<=1000)和M(0 <= M = 10000)。第二行中,有n个整数C1…CN。然后M行,每行有三个整数i,j,k(0 < <= j <= N,0≤k<2 ^ 31),这意味着从i到j营士兵总数量至少是K.

输出:

对于每一个测试案例,在一行一个整数,输出:根据陆逊的观察估算刘备军队最少的士兵数。然而,陆逊估计在输入数据可能很不精确。如果他的估计是不真实的,输出“Bad Estimations”在单一的一行。

解题思路:

题目要求从观察结果中求出刘备军队里士兵的最小值。由于每个营地都有能容纳士兵的最大值,存在不等约束关系,此题可以考虑用差分约束系统解题。

差分约束系统:

如果一个系统由n个变量和m个约束条件组成,其中每个约束条件都形如:

    Xj-Xi<=Bk   (i,j∈[1,n],k∈[1,m])

则称其为差分约束系统

求解差分约束系统:

求解差分约束系统,可以转化成图论的单源最短路径问题,

    Xj-Xi<=Bk

    Xj<=Xi+Bk

类似最短路径的三角不等式

distance[v]<= distance[u]+weight[u,v]

即distance[v] - distance[u]<=weight[u,v]

因此,以每个变量Xi为结点,对于约束条件

Xj-Xi<=Bk 即 Xj<=Xi+Bk

连接一条边(i,j),权值为Bk

再增加一个源点A,A与所有顶点相连,权值均为0,如果图中存在负环,则无解,否则,从源点到其它顶点的单源最短路径就是问题的一组可行解。

 

设有N个营地,编号为 i(i=1、2、3…N), K(i)代表第i(i=1、2、3…N)个营地的士兵数加上编号小于i的所有营地的士兵数。

则第j(j=1、2、3…N)个营地的士兵数为K(j)-K(j-1)

若M(j)表示营地j能容纳的最大士兵数,则有不等关系:

    K(j)-K(j-1)<=M(j)

变换得

    K(j)<=K(j-1)+M(j)

为了避免第一个营地的特殊情况,我们增加一个编号为0且士兵数为0的营地,即K(1)-K(0)=K(1).这个营地只是辅助,对结果不产生影响。

我们设每个编号i的营地为顶点i(i=0,1,2…N),我们添加一个源点,源点到所有其他顶点都有一条有向边,权值为0,不需要保存该点,只是辅助解题,保证图的连通性。则K(j)和K(j-1)分别为从源点到j,j-1两点的最短路径权值,若营地j的士兵数为M(j),则有K(j)<=K(j-1)+M(j),可以从j到j-1连接一条有向边,权值为M(j),问题转化为求从源点到顶点j的单源最短路径。

因为从源点到点j的最短路径必定满足K(j)<=K(j-1)+M(j)

同理,从营地i到j之间至少有X个士兵,则有不等关系:

    K(j)-K(i-1)>=X

变换为

    K(i-1)<=K(j)-X

于是,从顶点j到顶点i-1连一条有向边,权值为-X,求解源点到j点的最短路径时必定也满足该不等式K(i-1)<=K(j)-X

技术分享

 

其中K(1),K(2)…K(N)为差分约束系统的一组解,当答案要求非负时,我们对每个解加上一个常数C便可。

最后源点到点N的最短路径便是问题的解,但实际上士兵数不能为负,因为求最少的士兵总数,我们可以加上一个最小的常数C,使得该组解非负。

当图中存在负权环时,问题无解。因为若存在负权环,不停地走这个环的话,最短路径趋向负无穷,显然不可能存在最短路径。

该题涉及负权边,用Bellman Ford算法求解最短路径较合适。

Bellman Ford算法思想是基于以下事实:

“两个点间如果存在最短路径,那么每个结点最多经过一次。”

也就是说,对n个结点的图,这条路不超过n-1条边。

如果一个结点经过了两个,说明我们走了一个圈:

    如果该圈的权为正,显然不划算;

    如果是负圈,不存在最短路径;

    如果是零圈,去掉不影响最短路径。

路径边数上限为m的最短路径,可以通过由边数上限为m-1时的最短路径“外加一条边”来求,则最多只需要迭代n-1次就可以求出最短路径。

Bellman Ford算法伪代码如下:

//V(G)为图G的顶点集,E(G)为图G的边集

for i=0 to |V(G)|

   for 每条边(u,v)属于E(G)//松弛每条边

    if(distance[v]>distance[u]+weight[u,v])

      distance[v]=distance[u]+weight[u,v]

    end for

end for

for 每条边(u,v)属于E(G)//检查是否有负环

if(distance[v]>distance[u]+weight[u,v])

  return false

end for

return true

算法时间复杂度O(VE)。

该算法简单容易理解,缺点是时间复杂度较高,这里可以用队列进行优化,本文重点是最短路径算法如何应用在差分约束系统,便不再赘述。

 

Bellman Ford算法用到松弛技术:

维护一个源点到所有其它点的距离表distance[],松弛一条边(u,v)的过程包括测试我们是否可以通过结点u对目前到v结点的最短路径distance[v]进行修改,如果通过u到达v的路径更短,则更新distance[v]。这样便是保证每两个结点都有

distance[v]<= distance[u]+weight[u,v]

伪代码如下:

if(distance[v]> distance[u]+weight[u,v])//不符合就调整

    distance[v] = distance[u]+weight[u,v]

解题代码:

  1 #include<stdio.h>
  2 
  3 #include<vector>
  4 
  5 int *disList;//求最短路径时维护的距离表
  6 
  7 struct Edge{//边结构
  8 
  9        int start;//起点
 10 
 11        int end;//终点
 12 
 13        int weight;//权值
 14 
 15 };
 16 
 17 std::vector<Edge> E;//存放边集的容器
 18 
 19 std::vector<Edge>::iterator it;//迭代器
 20 
 21 bool bellmanford(int N)//求每个顶点到源点的最短路径
 22 
 23 {
 24 
 25  
 26 
 27        while(--N) //走N-1 条边
 28 
 29        {
 30 
 31               for (it = E.begin(); it != E.end();it++)//对每条边进行松弛
 32 
 33               {
 34 
 35                      if (it->weight + disList[it->start]< disList[it->end])
 36 
 37                      {
 38 
 39                             disList[it->end] = it->weight + disList[it->start];
 40 
 41                      }
 42 
 43               }
 44 
 45        }
 46 
 47        for (it = E.begin(); it != E.end(); it++)//若还能进行松弛,说明存在负环,无解
 48 
 49        {
 50 
 51               if (it->weight + disList[it->start]< disList[it->end])
 52 
 53               {
 54 
 55                      return false;
 56 
 57               }
 58 
 59        }
 60 
 61        return true;
 62 
 63 };
 64 
 65 void output(int N)
 66 
 67 {
 68 
 69        if (!bellmanford(N))printf("Bad Estimations\n");
 70 
 71        else//根据实际情况,兵营士兵数不能为负,将所有解化为正数
 72 
 73        {
 74 
 75               int min = disList[0];
 76 
 77               for (int i = 1; i <= N; i++)
 78 
 79               {
 80 
 81                      if (min > disList[i])min = disList[i];
 82 
 83               }
 84 
 85               if (min < 0)
 86 
 87               {
 88 
 89                      disList[N] = disList[N] - min;
 90 
 91               }
 92 
 93               printf("%d\n", disList[N]);
 94 
 95        }
 96 
 97 };
 98 
 99 int main()
100 
101 {
102 
103        int N, M;
104 
105        while (scanf_s("%d%d", &N,&M) ==2)
106 
107        {
108 
109               E.clear();//清空容器
110 
111               disList = new int[N + 1];
112 
113               for (int i = 0; i <= N; i++)//初始化距离表
114 
115               {
116 
117                      disList[i] = 0;
118 
119               }
120 
121               //输入数据
122 
123               Edge e;
124 
125               for (int i = 1; i <= N; i++)
126 
127               {
128 
129                      scanf_s("%d", &e.weight);
130 
131                      e.start = i - 1;
132 
133                      e.end = i;
134 
135                      E.push_back(e);//营地i的最大士兵数为w,向矩阵图插入由i-1指向i权值为w的有向边
136 
137               }
138 
139               int a, b,c;
140 
141               for (int i = 0; i < M; i++)
142 
143               {
144 
145                      scanf_s("%d%d%d", &a,&b,&c);
146 
147                      e.start = b;
148 
149                      e.end = a - 1;
150 
151                      e.weight = -c;
152 
153                      E.push_back(e);//营地a与营地b之间至少士兵数为c,向矩阵图中插入由点b指向点a,权值为-c的有向边
154 
155               }     
156 
157               output(N);//输出结果
158 
159               delete[]disList;//释放内存
160 
161        }
162 
163        getchar();
164 
165        return 0;
166 
167 }

 

ZOJ– 2770Burn the Linked Camp单源最短路径差分约束系统

标签:

原文地址:http://www.cnblogs.com/litianhan/p/4569834.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!