hdu1534 差分约束系统

本文详细解析了一道关于差分约束系统的题目,介绍了四种工程部分间的关系(FAS, SAF, FAF, SAS),并展示了如何将这些关系转化为数学表达式进行求解。文章还讨论了使用SPFA算法时需要注意的问题及解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1534

 

题目大意:一个工程被分为若干个部分,每个部分被完成都需要一定的时间,给定工程若干部分间开始或结束的先后关系,要求在最少的时间内完成工程。若能完成,输出每个部分开始的时间,如果不能,输出impossible

 

考查点:差分约束系统

 

思路:工程间的关系有四种,FAS SAF FAF SAS

       Sstart        Aafter         F:  finish

              FAS  x, y  表示x finish after y start

      既然是问每个部分的最早开始时间,那么我们就设start[i] 表示第i个部分的最早开

始时间。Time[i]表示i需要花费的时间。 则四种关系便可化解为一些表达式

      FAS  x,y  :  start[x] + time[x] >= start[y]

      SAF  x,y  :  start[x] >= start[y] + time[y]

      FAF  x,y  :  start[x] + time[x] >= start[y] + time[y]

      SAS  x,y  :  start[x] >= start[y]

      有了这些等式,我们便可以工程部分与部分之间的关系表示出来,并且,由于有这些

表达式关系,并且题目是要求工程尽量早的完成,既求每个部分的最早开始时间,因

为开始的越早,整个工程就会结束的越早,因此进一步化解等式:

Start[x] >= start[y] - time[x];

Start[x] >= start[y] + time[y];

Start[x] >= start[y] + time[y] – time[x];

Start[x] >= start[y] + 0;

为每一个start求一个下界。分析到这一步,用脚后跟都能想到可以用差分约束系统

来搞此题。

差分约束系统是通过解最短路或最长路来实现的。此题求的是每个start的下界,故

我们用求最长路的方法(d[u] < d[v] + (u,v)

这样我们每个表达式可统一为start[x] >= start[y] + c 的形式  我们只要从yx引一

条长为c的边即可。  但是这样的建图用bellman是可以AC的,初始化时只要让每

start为零即可,但是这样做把算法改为SPFA就不对了,这是为什么呢?我们来看

这样一组数据,

2

1

2

SAF 1 2

#

正确的输出应该是

1 2

2 0

但是如果直接用SPFA的话,结果是

1 0

2 0

问题出在初始化上,用bellman时,算法本身会扫描每一条边,因此没有所谓的出发

点,但是SPFA不同,SPFA是由一个最初始的点开始拓展的。如果我们从1开始,那

么,2根本就不会被扩展到。而且有1开始扩展是没有理由的,因为体力没有告诉你

1肯定最先开始,这个用例就是2先开始的,如何纠正这个错误,这就用到了类似于

网络流里加源点的思想。我们加一个点只想每一个部分,边权为0,这样的话,初始

start-maxlongint  从零开始拓展就可以保证每个点至少被拓展一次,这样就不

会出现之前的情况了

 

提交情况: wrong answer 无数次

              Accepted     一次

 

收获: 对差分约束系统的题分析上有了进一步的认识。也对建图有一定的感悟了

 

经验: 一定要分析各种情况,不能妄下结论,不能心浮气躁,冲动是魔鬼

 

//AC code

 

#include <stdio.h>

#include <string.h>

 

#define MAXN 10000

#define MAXM 10000000

#define INF 2100000000

struct NODE{

    long to;

    long le;

    long next;

};

NODE edges[MAXM];

long ad;

long point[MAXN];

long time[MAXN], start[MAXN];

 

int SPFA(long n){

    long i, vis[MAXN], In_times[MAXN], queue[MAXN];

    memset(vis, 0, sizeof(vis));

    memset(In_times, 0, sizeof(In_times));

    for(i = 0; i <= n; i ++)

       start[i] = -INF;

    long head = 0, tail = 1;

    queue[head] = 0, vis[0] = 1, In_times[0] = 1, start[0] = 0;

    while(head != tail){

       long temp = queue[head];

       vis[temp] = 0;

       head = (head + 1) % MAXN;

       if(In_times[temp] > n) return 0;

       for(int p = point[temp]; ~p; p = edges[p].next){

           long v = edges[p].to;

           if(start[v] < start[temp] + edges[p].le){

              start[v] = start[temp] + edges[p].le;

              if(!vis[v]){

                  queue[tail] = v;

                  vis[v] = 1;

                  In_times[v] ++;

                  tail = (tail + 1) % MAXN;

              }

           }

       }

    }

    return 1;

}

 

int main(){

    long n, i, x, y, t = 0;

    char str[10];

    while(~scanf("%ld", &n), ++t , n){

       for(i = 1; i <= n; i ++)

           scanf("%ld", &time[i]);

       ad = 0;

       memset(point, -1, sizeof(point));

       while(~scanf("%s", str), str[0] != '#'){

           scanf("%ld%ld", &x, &y);

           edges[ad].to = x; edges[ad].next = point[y]; 

           if(strcmp(str,"SAF") == 0)

              edges[ad].le = time[y];

           else

              if(strcmp(str,"FAF") == 0)

                  edges[ad].le = time[y] - time[x];

              else

                  if(strcmp(str,"FAS") == 0)

                     edges[ad].le = - time[x];

                  else edges[ad].le = 0;

           point[y] = ad ++;

       }

       for(i = 1; i <= n; i ++){

           edges[ad].to = i;

           edges[ad].le = 0;

           edges[ad].next = point[0];

           point[0] =ad ++;

       }

       printf("Case %ld:\n", t);

       if(SPFA(n))

           for(i = 1; i <= n; i ++)

              printf("%ld %ld\n", i, start[i]);

       else

           printf("impossible\n");

       printf("\n");

    }

    return 0;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值