POJ1201 - 再深入了解差分约束与SPFA

  题意就是给出每段至少有几个数如ai到bi至少用ci个数....问整个集合至少需要多少个数才能满足所有的条件...ci <= bi - a1 + 1...

  转化一下...Sk代表不大于k有多少个数...那么题目的条件就转化为一组 Sbi - S(ai-1) >= ci了..

  分析题目给出的不等式...以及题目的"至少需要"..也就是要求的是一组最小非负解...

  传统的用SPFA解差分约束..是给出的一组或者说要化成一组 a - b <= k 的形式...然后来构边.. a为终点,b为起点..权值为k...用SPFA求出的是确定一个数后的最大解..

  但这道题是要求的是最小解...这里就要更加的深入理解用SPFA解差分约束了..

  之所以能用SPFA来解差分约束...就是SPFA在relax时是

            d [ line.end ] >= d [ line.start ] + line.wight 

     更新完后..所有的 d [ i ] 都会满足这个条件..

  再看差分约束系统每一项的形式 

             a - b <= k    稍微转化一下..

   就是  b>=a-k ...

             如果把 b作为 line.end..a作为 line.start..k作为line.wight...那么就和SPFA的relax一模一样...

  SPFA作完后能保证所有的 d [ line.end ] >= d [ line.start ] + line.wight ...自然就能保证所有的不等式成立... 

  所以将每一项的 b 作为线段的终点,a作为线段的起点,k作为线段的权值...定一个源点(相当于给某一个点一个确定值)然后做一次SPFA..就能得到一组满足所有条件的解(这里不讨论存在负环的情况)...并且这个解是确定了某个值后的最大一组解( 所有的和相加最大...这里还没理解...)..

  回到这道题...给出的每一项是 a - b >=k 可以将两边同时乘负一得到 b - a <= -k 也可以干脆就把 SPFA 的Relax 改成d [ line.end ] <= d [ line.start ] + line.wight 来求解...因为同理a - b >=k 可以写成 b <= a + k.... 同样的b 作为线段的终点,a作为线段的起点,k作为线段的权值

  我是用的后面一种方法...SPFA的Relax改了以后~~做出的一组解首先会是满足所有约束条件的...并且这组解是在确定了某个点的值下最小的一组解(和原始的那种正好相反)...这道题肯定就是把最小的点赋值为0然后来求..做出来直接就是题目所要求一组最小的非负解了...答案也就是在 d [ maxdata ] 里...

   这道题有两个隐性条件一定要用上...第一个就是 Sk - S(k-1) >=0 相邻两个数的差是大于等于0的...Sk - S(k-1) <=1 相邻两个数的差是小于等于1的...要加上这些约束条件..也就是加上这些边..

    题目的处理的方面...因为题目的数列是从0开始的...但在沟成约束条件是...如输入告诉说 1 到 3 至少有1个数..那转化成不等式应该是 S3 - S0 >=1...那如果给出的是 0 到 3 至少有一个数呢??难道  S3 - S-1 >=1 ??明显下标越界..所以干脆把数列向右平移一个单位..让其从1开始就行了...还有一点就是我在程序中用minnum和maxnum分别记录这组约束条件中出现的最小数和最小数,减少不必要的操作...

    Runtime error的...要注意边的个数..不止题目给的50000...因为自己要构更多的边...就是那两个隐形条件的边...所以至少需要开150001的大小..

Progarm:

#include<iostream>
#include<queue>
#define MAXN 200001
#define oo 0x7F
using namespace std;
struct p1
{
    int x,y,k,next;      
}line[MAXN];
int i,n,m,link[MAXN],x,y,k,minnum,maxnum; 
queue<int> myqueue;
int SPFA()
{ 
   int i,k,h,d[MAXN];
   bool used[MAXN];  
   while (!myqueue.empty()) myqueue.pop();
   memset(d,-0x7F,sizeof(d));
   memset(used,false,sizeof(used));
   d[minnum]=0; myqueue.push(minnum); 
   while (!myqueue.empty())
   {
       h=myqueue.front();
       myqueue.pop();
       used[h]=false;
       k=link[h];
       while (k)
       {
           if (d[line[k].y]<d[h]+line[k].k)
           {
                d[line[k].y]=d[h]+line[k].k;
                if (!used[line[k].y])
                {
                    used[line[k].y]=true;
                    myqueue.push(line[k].y);                     
                }                          
           }          
           k=line[k].next;   
       }  
   } 
   return d[maxnum];
}
int main()
{ 
   while (~scanf("%d",&n))
   {
       memset(link,0,sizeof(link));
       memset(line,0,sizeof(line)); 
       m=0;
       minnum=oo; maxnum=-oo;
       while (n--)
       { 
           scanf("%d%d%d",&x,&y,&k);  
           y++;  
           if (x<minnum) minnum=x;
           if (y>maxnum) maxnum=y;
           m++; 
           line[m].x=x; line[m].y=y; line[m].k=k;     
           line[m].next=link[line[m].x]; link[line[m].x]=m;         
       } 
       int k;
       for (i=minnum;i<maxnum;i++)
       {
           m++;
           line[m].x=i+1; line[m].y=i; line[m].k=-1;
           line[m].next=link[line[m].x]; link[line[m].x]=m;   
           m++;
           line[m].x=i; line[m].y=i+1; line[m].k=0;
           line[m].next=link[line[m].x]; link[line[m].x]=m;               
       }
       printf("%d\n",SPFA());
   } 
   return 0;   
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值