差分约束问题及其一个简单应用案例

如果一个系统由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
  在实际的应用中,一般使用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)的系数一定要是正的。而常量则不一定。

 

  一个简单案例:

Burn the Linked Camp
Time Limit: 2 Seconds Memory Limit: 65536 KB

It is well known that, in the period of The Three Empires, Liu Bei, the emperor of the Shu Empire, was defeated by Lu Xun, a general of the Wu Empire. The defeat was due to Liu Bei's wrong decision that he divided his large troops into a number of camps, each of which had a group of armies, and located them in a line. This was the so-called "Linked Camps".

Let's go back to that time. Lu Xun had sent many scouts to obtain the information about his enemy. From his scouts, he knew that Liu Bei had divided his troops into n camps, all of which located in a line, labeled by 1..n from left to right. The ith camp had a maximum capacity of Ci soldiers. Furthermore, by observing the activities Liu Bei's troops had been doing those days, Lu Xun could estimate the least total number of soldiers that were lived in from the ith to the jth camp. Finally, Lu Xun must estimate at least how many soldiers did Liu Bei had, so that he could decide how many troops he should send to burn Liu Bei's Linked Camps.

Input:

There are multiple test cases! On the first line of each test case, there are two integers n (0<n<=1,000) and m (0<=m<=10,000). On the second line, there are n integers C1……Cn. Then m lines follow, each line has three integers i, j, k (0<i<=j<=n, 0<=k<2^31), meaning that the total number of soldiers from the ith camp to the jth camp is at least k.

Output:

For each test case, output one integer in a single line: the least number of all soldiers in Liu Bei's army from Lu Xun's observation. However, Lu Xun's estimations given in the input data may be very unprecise. If his estimations cannot be true, output "Bad Estimations" in a single line instead.

Sample Input:

3 2
1000 2000 1000
1 2 1100
2 3 1300
3 1
100 200 300
2 3 600


Sample Output:

1300
Bad Estimations

 

 

  题目大意:有n个兵营,每个兵营最多有Ci个士兵(1<=i<=n),然后有m次侦查,每次侦查得到一组数据:从第i个兵营到第j个兵营最少有k个士兵。问最少一共有多少士兵。

  用dist[i]记录从第1个到第i个兵营一共最少的士兵数目,则题目给出的差分约束条件为:

  dist[j]-dist[i-1]>=k;

  dist[i+1]>=dist[i];

  dist[i]<=dist[i-1]+Ci;   (即dist[i-1]>=dist[i]-Ci)

  由以上三个约束条件构图,dist全部初始化为0,求最长路后dist[n]即所求。参考代码如下:

  1 #include <cstdio>
  2 #include <queue>
  3 #include <climits>
  4 using namespace std;
  5 
  6 const int MAX_EDGE=10010;
  7 const int MAX_VER=1010;
  8 struct Edge
  9 {
 10     int x;
 11     int value;
 12     int next;
 13 };
 14 Edge edge[MAX_EDGE];
 15 int visited[MAX_VER], dis[MAX_VER], adj[MAX_VER], mark[MAX_VER], n, m, c[MAX_VER], edgeNum;
 16 
 17 bool SPFA()
 18 {
 19     int cur;
 20     queue<int> q;
 21     for(int i=0; i<=n; i++)
 22     {
 23         visited[i] = 0;
 24         mark[i] = 0;
 25         dis[i] = 0;
 26     }
 27     for(int i=0; i<=n; i++)
 28     {
 29         q.push(i);
 30         visited[i] = 1;
 31         mark[i]++;
 32     }
 33     while(!q.empty())
 34     {
 35         cur = q.front();
 36         q.pop();
 37         int tmp = adj[cur];
 38         visited[cur] = 0;
 39         while(tmp != -1)
 40         {
 41             if(dis[edge[tmp].x] < dis[cur] + edge[tmp].value)
 42             {
 43                 dis[edge[tmp].x] = dis[cur] + edge[tmp].value;
 44                 if(visited[edge[tmp].x] == 0)
 45                 {
 46                     q.push(edge[tmp].x);
 47                     visited[edge[tmp].x] = 1;
 48                     if((++mark[edge[tmp].x])>n)
 49                         return false;
 50                 }
 51             }
 52             tmp = edge[tmp].next;
 53         }
 54         if(cur>1 && dis[cur-1]<dis[cur]-c[cur])
 55         {
 56             dis[cur-1] = dis[cur] - c[cur];
 57             if(visited[cur-1]==0)
 58             {
 59                 q.push(cur-1);
 60                 visited[cur-1] = 1;
 61                 if((++mark[cur-1])>n)
 62                     return false;
 63             }
 64         }
 65         if(cur<n && dis[cur+1]<dis[cur])
 66         {
 67             dis[cur+1] = dis[cur];
 68             if(visited[cur+1]==0)
 69             {
 70                 q.push(cur+1);
 71                 visited[cur+1] = 1;
 72                 if((++mark[cur+1])>n)
 73                     return false;
 74             }
 75         }
 76     }
 77     return true;
 78 }
 79 
 80 void addEdge(int u, int v, int w)
 81 {
 82     edge[edgeNum].x = v;
 83     edge[edgeNum].value = w;
 84     edge[edgeNum].next = adj[u];
 85     adj[u] = edgeNum++;
 86 }
 87 
 88 int main()
 89 {
 90     int i, u, v, w;
 91     while(~scanf("%d %d", &n, &m))
 92     {
 93         for(i=1; i<=n; i++)
 94         {
 95             scanf("%d", &c[i]);
 96             adj[i] = -1;
 97         }
 98         adj[0] = -1;
 99         for(edgeNum=i=0; i<m; i++)
100         {
101             scanf("%d %d %d", &u, &v, &w);
102             addEdge(u-1, v, w);
103         }
104         if(SPFA())
105             printf("%d\n", dis[n]);
106         else
107             puts("Bad Estimations");
108     }
109     return 0;
110 }

转载于:https://www.cnblogs.com/n-u-l-l/archive/2012/07/24/2606912.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值