UVa 515 King (差分约束系统)

 

UVa 515 King (差分约束系统)

分类: ACM_图论 ACM_UVa   79人阅读  评论(0)  收藏  举报

差分约束系统:

如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。

        差分约束可以转化话单源最短路求解。因为单源最短路径满足三角不等式d[v] <= d[u] + w(u, v), 这里的 <= 可以为改 >= 只要改变一下初始化条件即可。

 

        差分约束题目有两种,一种求最大值,另外一种求最小值。

       (1)当题目是求满足给定不等式的最小值时,就是求图的最长路。

        建图方式如下:a – b >= c,对应于边b –> a w(b, a) = c, 然后求最短路;判断条件是:d[v] <= d[u] + w(u, v), 初始化d[]为-INF. 这样求出的d[]就是满足条件的最小值。原因很简单,因为d[i] 是从-INF开始增大,根据不等式逐渐增大,当满足所有不等式时,那么d[i]肯定是最小的了。

       (2)当题目是求满足给定不等式的最大值时,就是求图的最短路。

        建图方式如下:a – b <= c,对应于边b –> a w(b, a) = c, 然后求最长路;判断条件是:d[v] >= d[u] + w(u, v), 初始化d[]为INF.这样求出的d[]就是最大值。原因和上面一样,因为是从INF逐渐减小的,当满足完所有条件时,就停止。那么d[i]是最大的了。

 

       常见单源最短路径方法有bellman_ford, spfa, Dijkstra

       bellman_ford算法,适用于边值负权的图,还能判断是否存在负权回路,它是把所有的边都松弛n-1次。算法复杂度为O(VE),这个方法效率不高,在数据规模大时,往往会TLM。

       spfa算法,是bellman_ford算法的改进版。它通过一个队列或栈,来保存需要更新的顶点。它减去了bellman算法很多不必要的松弛,因此它的效率比bellman要好,为O(ke)。

       Dijkstra算法,它只适用于边值为非负的图上。它基于贪心,每次选取最短的边。可以用heap来优化,在正权图也可以考虑。

 

       体会:虽然bellman_ford不及spfa的效率,但它在判断是否存在负权回路比spfa方便。它能判断不连通的图,而spfa判断不连通图时有可能不准确,当开始的源点到达不了某些顶点时,那么那些顶点就无法检验了。为此,spfa需要添加一个超源点来使得图连通(还有一个方法:就是先把所有顶点入队),而bellman不需要添加超源点, 因为它是对图的每一条边都进行n-1次松弛。有时候超源点不好添加,这时可以考虑用bellman。

       还有一点,当适用spfa添加超源点使得图连通(超源点S0 到其他顶点距离都为0)时,用最短路求出的解, 除了满足给定的不等式外,还会满足下列的不等式:

        S1 – S0 <= 0

        S2 – S0 <= 0

        …

        Sn – S0 <= 0

也就是说S1 ~ Sn是 < 0 的,这个对不同问题,要具体分析。这些额外满足的不等式,不能与题目要求的矛盾。


SPFA判负圈需要图联通,所以要加个超源点连上所有的点。而Bellman判负圈是对每一条边进行松弛,可以直接判。


SPFA code:


[cpp]  view plain copy
  1. /* ********************************************** 
  2. Author      : JayYe 
  3. Created Time: 2013/10/7 15:26:16 
  4. File Name   : Orz.cpp 
  5. *********************************************** */  
  6.   
  7. #include <stdio.h>  
  8. #include <string.h>  
  9. #include <algorithm>  
  10. using namespace std;  
  11.   
  12. const int maxn = 100 + 5;  
  13. const int INF = 1<<30;  
  14.   
  15. struct Edge{  
  16.     int u, to, next, val;  
  17. }edge[maxn*maxn];  
  18.   
  19. int head[maxn], d[maxn], vis[maxn], T[maxn], q[maxn*maxn], n, E;  
  20.   
  21. void init() {  
  22.     memset(head, -1, sizeof(head));  
  23.     E = 0;  
  24. }  
  25.   
  26. void newedge(int u, int to, int val) {  
  27.     edge[E].u = u;  
  28.     edge[E].to = to;  
  29.     edge[E].val = val;  
  30.     edge[E].next = head[u];  
  31.     head[u] = E++;  
  32. }  
  33.   
  34. bool SPFA() {  
  35.     for(int i = 0;i <= n + 1; i++)  {  
  36.         d[i] = INF; vis[i] = T[i] = 0;  
  37.     }  
  38.     T[n+1] = 1;  
  39.     d[n+1] = 0;  
  40.     vis[n+1] = 1;  
  41.     int st = 0, ed = 0;  
  42.     q[ed++] = n+1;  
  43.     while(st < ed) {  
  44.         int u = q[st++];  
  45.         vis[u] = 0;  
  46.         for(int i = head[u];i != -1;i = edge[i].next) {  
  47.             int to = edge[i].to, val = edge[i].val;  
  48.             if(d[to] > d[u] + val) {  
  49.                 d[to] = d[u] + val;  
  50.                 if(!vis[to]) {  
  51.                     vis[to] = 1;  
  52.                     T[to]++;  
  53.                     if(T[to] > n + 1)   return true;  
  54.                     q[ed++] = to;  
  55.                 }  
  56.             }  
  57.         }  
  58.     }  
  59.     return false;  
  60. }  
  61.   
  62. int main() {  
  63.     int m;  
  64.     while(scanf("%d", &n) != -1 && n) {  
  65.         init();  
  66.         scanf("%d", &m);  
  67.         for(int i = 0;i < m; i++) {  
  68.             int fr, len, k;  
  69.             char op[4];  
  70.             scanf("%d%d%s%d", &fr, &len, op, &k);  
  71.             int u = fr + len, to = fr-1;  
  72.             if(op[0] == 'g')  
  73.                 newedge(u, to, -1 - k);  
  74.             else  
  75.                 newedge(to, u, k - 1);  
  76.         }  
  77.         for(int i = 0;i <= n; i++)  newedge(n+1, i, 0);  
  78.         printf("%s\n", SPFA() ? "successful conspiracy" : "lamentable kingdom");  
  79.     }  
  80.     return 0;  
  81. }  

Bellman code:

[cpp]  view plain copy
  1. /* ********************************************** 
  2. Author      : JayYe 
  3. Created Time: 2013/10/7 15:26:16 
  4. File Name   : Orz.cpp 
  5. *********************************************** */  
  6.   
  7. #include <stdio.h>  
  8. #include <string.h>  
  9. #include <algorithm>  
  10. using namespace std;  
  11.   
  12. const int maxn = 100 + 5;  
  13.   
  14. struct Edge{  
  15.     int u, to, next, val;  
  16. }edge[maxn*maxn*2];  
  17.   
  18. int head[maxn], d[maxn], n, E;  
  19.   
  20. void init() {  
  21.     memset(head, -1, sizeof(head));  
  22.     E = 0;  
  23. }  
  24.   
  25. void newedge(int u, int to, int val) {  
  26.     edge[E].u = u;  
  27.     edge[E].to = to;  
  28.     edge[E].val = val;  
  29.     edge[E].next = head[u];  
  30.     head[u] = E++;  
  31. }  
  32.   
  33. bool Bellman() {  
  34.     memset(d, 0, sizeof(d));  
  35.     for(int i = 0;i < n+1; i++) {  
  36.         for(int j = 0;j < E; j++) {  
  37.             int u = edge[j].u, to = edge[j].to, val = edge[j].val;  
  38.             if(d[to] > d[u] + val) {  
  39.                 d[to] = d[u] + val;  
  40.                 if(i == n)  return true;  
  41.             }  
  42.         }  
  43.     }  
  44.     return false;  
  45. }  
  46.   
  47. int main() {  
  48.     int m;  
  49.     while(scanf("%d", &n) != -1 && n) {  
  50.         init();  
  51.         scanf("%d", &m);  
  52.         for(int i = 0;i < m; i++) {  
  53.             int fr, len, k;  
  54.             char op[4];  
  55.             scanf("%d%d%s%d", &fr, &len, op, &k);  
  56.             int u = fr + len, to = fr-1;  
  57.             if(op[0] == 'g')  
  58.                 newedge(u, to, - 1 - k);  
  59.             else  
  60.                 newedge(to, u, k - 1);  
  61.         }  
  62.         printf("%s\n", Bellman() ? "successful conspiracy" : "lamentable kingdom");  
  63.     }  
  64.     return 0;  
  65. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值