[poj 1364]King[差分约束详解(续篇)][超级源点][SPFA][Bellman-Ford]

 

[poj 1364]King[差分约束详解(续篇)][超级源点][SPFA][Bellman-Ford]

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

目录(?)[+]

题意

有n个数的序列, 下标为[1.. N ], 限制条件为: 下标从 si 到 si+ni 的项求和 < 或 > ki. 一共有m个限制条件. 问是否存在满足条件的序列.

思路

转化为差分约束, 就是

即 Si 为第 i 项的前缀和, 特别的 So 为0. 转化不等式(连续子段和变为前缀和之差 > < 变为 >= <= ),求最短路, 判断有没有负权回路.

注意

由于并不知道图是否连通

(不像是之前的那道Candies图一定是联通的,选择班长所代表的点即可)

所以正常情况下是要另设一个"超级源点", 连接图上的每个点, 从这个点出发就一定可以遍历到每一个点.

"超级源点"到每个点的边权是任意的,而它自己的点权自然是0.

这样的话,就求出了一组满足每对点的差尽可能大, 并且其中的d[0] = 0的解.

1. 将所有点(包括"超级源点")同时平移, 均为满足所有约束的可行解(包括新加入的边权们)

2. 将原图中的所有点同时平移, 得到所有满足原有约束的可行解. 但是仍有d[0] = 0的此时, 与超级源点的这些约束有可能不满足. 但是显然这是无所谓的.

3. 由此可知, 超级源点的作用就在于确保图的连通性,使得每一个点都有一个"距离". 而"超级源点"带来的额外约束一是d[0] = 0, 二是新加的边权. 二者影响的都是d[1]到d[n]的浮动情况(d[0]是参考零点, 额外的边权约束则是起到了限制d[1]到d[n]与d[0]的距离的作用,一堆不等式同样是选择了限制最严的那些并且距离尽可能大....没有实际意义...)

总之参考零点就是这样~

但是用SPFA只是判断负环的话,只需要初始时将所有点入队(而非只将源点入队), 然后判断每个点的入队次数. 如果超过点的总数, 说明存在负环.否则不存在.

数值上是从INF开始减, 有负环的话就会一直减... 没有的话就会正常退出, 当然这个时候d[ ] 值会很大..


SFPA + stack

[cpp]  view plain copy
  1. //132K 16MS  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <stack>  
  5. using namespace std;  
  6. const int MAXN = 105;  
  7. const int MAXE = 105;  
  8. const int INF = 0x3f3f3f3f;  
  9. struct pool  
  10. {  
  11.     int v,pre,w;  
  12. } p[MAXE];  
  13. int num,head[MAXN],d[MAXN],n,m,enq[MAXN];  
  14. bool inq[MAXN];  
  15. stack<int> s;  
  16. void clear()  
  17. {  
  18.     while(!s.empty())   s.pop();  
  19.     memset(head,0,sizeof(head));  
  20.     memset(d,0x3f,sizeof(d));  
  21.     memset(inq,false,sizeof(inq));  
  22.     memset(enq,0,sizeof(enq));  
  23.     num = 0;  
  24. }  
  25.   
  26. bool SPFA()  
  27. {  
  28.     for(int i=0;i<=n;i++)  
  29.     {  
  30.         s.push(i);  
  31.         inq[i] = true;  
  32.         enq[i]++;  
  33.     }  
  34.     d[0] = 0;  
  35.     while(!s.empty())  
  36.     {  
  37.         int u = s.top();  
  38.         s.pop();  
  39.         inq[u] = false;  
  40.         for(int tmp=head[u],v;v=p[tmp].v,tmp;tmp=p[tmp].pre)  
  41.         {  
  42.             int w = p[tmp].w;  
  43.             if(d[v]>d[u]+w)  
  44.             {  
  45.                 d[v] = d[u] + w;  
  46.                 if(!inq[v])  
  47.                 {  
  48.                     inq[v] = true;  
  49.                     enq[v]++;  
  50.                     if(enq[v]>n+1)    return false;  
  51.                     s.push(v);  
  52.                 }  
  53.             }  
  54.         }  
  55.     }  
  56.     return true;  
  57. }  
  58.   
  59. void add(int u, int v ,int w)  
  60. {  
  61.     p[++num].v = v;  
  62.     p[num].w = w;  
  63.     p[num].pre = head[u];  
  64.     head[u] = num;  
  65. }  
  66.   
  67. int main()  
  68. {  
  69.     while(scanf("%d",&n)==1 && n)  
  70.     {  
  71.         clear();  
  72.         scanf("%d",&m);  
  73.         while(m--)  
  74.         {  
  75.             int si,ni,ki;  
  76.             char o,p;  
  77.             scanf("%d %d %c%c %d",&si,&ni,&o,&p,&ki);  
  78.             if(o=='g')  add(si+ni,si-1,-ki-1);  
  79.             else        add(si-1,si+ni,ki-1);  
  80.         }  
  81.         printf(SPFA()?"lamentable kingdom\n":"successful conspiracy\n");  
  82.     }  
  83. }  

用Bellman-Ford也可以.这个时候就要用到超级源点啦

[cpp]  view plain copy
  1. //120K 0MS  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. using namespace std;  
  5. const int MAXN = 105;  
  6. const int MAXE = 210;  
  7. const int INF = 0x3f3f3f3f;  
  8. int s[MAXE],e[MAXE],w[MAXE];  
  9. int num,d[MAXN],n,m;  
  10.   
  11. void clear()  
  12. {  
  13.     memset(d,0x3f,sizeof(d));  
  14.     num = 0;  
  15. }  
  16.   
  17. bool Bellman_Ford()  
  18. {  
  19.     d[n+1] = 0;  
  20.     for(int i=0;i<=n+1;i++)  
  21.     {  
  22.         for(int j=1;j<=num;j++)  
  23.         {  
  24.             if(d[e[j]]>d[s[j]]+w[j])    d[e[j]] = d[s[j]] + w[j];  
  25.         }  
  26.     }  
  27.     for(int j=1;j<=num;j++)  
  28.     {  
  29.         if(d[e[j]]>d[s[j]]+w[j])    return false;  
  30.     }  
  31.     return true;  
  32. }  
  33.   
  34. void add(int u, int v ,int c)  
  35. {  
  36.     s[++num] = u;  
  37.     e[num] = v;  
  38.     w[num] = c;  
  39. }  
  40.   
  41. int main()  
  42. {  
  43.     while(scanf("%d",&n)==1 && n)  
  44.     {  
  45.         clear();  
  46.         scanf("%d",&m);  
  47.         while(m--)  
  48.         {  
  49.             int si,ni,ki;  
  50.             char o,p;  
  51.             scanf("%d %d %c%c %d",&si,&ni,&o,&p,&ki);  
  52.             if(o=='g')  add(si+ni,si-1,-ki-1);  
  53.             else        add(si-1,si+ni,ki-1);  
  54.         }  
  55.         for(int i=0;i<=n;i++)  
  56.         {  
  57.             add(n+1,i,0);  
  58.         }  
  59.         printf(Bellman_Ford()?"lamentable kingdom\n":"successful conspiracy\n");  
  60.     }  
  61. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值