差分约束系统

差分约束的模型简单概括为:存在n个约束条件(这n个条件有些题目给出,有些为了使得运用最短路算法需要自己去添加),求满足这n个条件的一个集合,让这个集合的个数最小或者最大。

题目:

POJ

1201 题目意思是输入两个数a,b和这个两个数之间(包括a和b)至少需要存在c个数。例如3 7 3题目要求3到7之间要至少有3个数。题目要求满足所有输入条件的一个集合,使得这个集合里面的数字最少。那么我们会想到,我们可以设一个数组保存当前这个数字之前至少需要多少个数(可以理解成距离)例如3 7 3,3到7需要3个数,那么8之前需要的个数(距离)为dist[8]=3;当再输入8 11 2,8到11需要两个数,那么12之前就至少需要3+2=5个数字,这个刚好满足最短路的更新(松弛)操作。我们可以刚开始初始化每个数之前需要存在的数的个数dist为-1,如果输入a b c那么就在a和b+1之间搭一条边dist[a][b+1]=c(a和b+1之间需要存在c个数字),那么如果存在dist[u]+dist[u][v]>dist[v]的时候,dist[v]就需要更新(松弛)=>dist[v]=dist[u]+dist[v]。那是不是就这样建边然后运用最短路求就行了呢?不是的,例如dist[8]=5,dist[10][15]=3,dist[15]=6,从这里可以看出15之前至少需要5+3=8>6,需要8个数字,但是这里无法更新到15,因为8和10之间断节,8和10连不上,那么8前面要有5个数,那可以想到8后面的数也至少需要5个数,解决办法是在每个数字和他后面的数字搭上一条0的边就行了,那样9和10都能被更新。从而使得整个图能连接起来不断开,就以为这样行了,提交依然WA,原来有个很容易漏掉的,那就是i和i-1这两个点也需要搭边,那么i之前需要x个点, 那么i-1之前肯定至少需要x-1个点了,那么再加上一条边i和i-1的边的权值为-1,好一共3个约束条件,图就完美的连接起来了,直接spfa搞掉,代码:

#include<iostream> #include<queue> using namespace std; const int N = 160005; const int maxint = 0x3f3f3f3f; struct Edges { int s,e; int w; int next; Edges(int s1,int e1,int w1,int next1){s=s1;e=e1;w=w1;next=next1;} Edges() {} }; Edges ET1[N]; int EH1[N]; __int64 dist[N]; int n,m; int tag[N]; void addedges(int s,int e,int w,int &tot,int *EH,Edges *ET) { ET[tot]=Edges(s,e,w,EH[s]); EH[s]=tot; tot++; } void spfa(int s,int *EH,Edges *ET) { int p,k,tempe,tempw; queue<int>Q; memset(tag,0,sizeof(tag)); memset(dist,-1,sizeof(dist)); dist[s]=0; tag[s]=1; Q.push(s); while(!Q.empty()) { k=Q.front(); Q.pop(); tag[k]=0; for(p=EH[k];p!=-1;p=ET[p].next) { tempe=ET[p].e; tempw=ET[p].w; if(dist[k]+tempw>dist[tempe]) { dist[tempe]=dist[k]+tempw; if(tag[tempe]==0) { Q.push(tempe); tag[tempe]=1; } } } } } void solve() { int a,b,i,c,tot,max=0,min=maxint; memset(EH1,-1,sizeof(EH1)); tot=0; for(i=0;i<n;i++) { scanf("%d %d %d", &a, &b, &c); max=max>b+1?max:b+1; min=min<a?min:a; addedges(a,b+1,c,tot,EH1,ET1);//第一种情况的边 } for(i=min;i<=max;i++) { addedges(i,i+1,0,tot,EH1,ET1);//i到他后面的点的权为0,把他们连接起来 addedges(i+1,i,-1,tot,EH1,ET1);//每个点也需要和他前面的点搭边,权值为-1 } spfa(min,EH1,ET1); printf("%d/n",dist[max]); } int main() { cin>>n; solve(); return 0; }

1716  这题和1201几乎完全一样,1201懂了这题就是送,没啥好说的了

1275  这题刚开始搞搞死也不会怎么建边,只能投降看解题报告,因为说法几乎都是一样,过程很复杂需要自己慢慢的理清,总之一句话就是找约束条件。

     题意:一家24小时营业的超市,需要一批出纳员来满足它的需求,该超市在每天的不同时刻需要不同数目的出纳员来为顾客提供服务,现在给出一天里每一小时需要出纳员的最少数量……r[0],r[1],……r[23].r[0]表示从午夜到上午1:00需要出纳员的最少数目等等,每一天这些数据都是相同的,有n个人申请这项工作,每个申请者i在每天24小时中,从某一个特定的时刻开始连续工作恰好8小时,定义t[i(0<=t[i]<=23)为上面提到的开始时刻,也就是说,如果第i个申请者被录用,他将从t[i]时刻开始连续工作8小时.输入r[i]和t[i],计算为满足上述限制需要雇佣的最少出纳员数目.注意在每一时刻可以有比对应的r[i]更多的出纳员在工作.
     r[0……23]……每个时刻需要的出纳员数目
     t[0……23]……每个时刻应征的申请者数目
   求s[0……23]……s[i]表示0……i时刻雇佣的出纳员总数,s[i]-s[i-1]就是i时刻录用的出纳员数目,设s[-1]=0,sum为雇佣的所有出纳员总数,那么一个可行方案应该满足:
      s[i]-s[i-1]>=0       即在i时刻录用的出纳员数目大于或等于0
      s[i]-s[i-1]<=t[i]    即在i时刻录用的出纳员数目应该小于在i时刻申请者数目
      s[23]-s[-1]>=sum     
      s[i]-s[j]>=r[i]      此时i>j&&i=(j+8)%24 因为在0……j时刻雇佣的出纳员连续工作8个小时,此时i=j+8很显然需要重新雇佣出纳员且最小为r[i]
      s[j]-s[i]<=sum-r[i] 此时i<j&&i=(j+8)%24 因为j>i说明可能某一天雇佣的出纳员连续工作8个小时后到了下一天的i时刻,所以从i……j=16+i时刻需要雇佣的出纳员数目最大为sum-r[i]
   由于sum是未知的,所以可以根据上述约束条件来构造约束图,为方便建图,以0为起点,由于题目要求的是出纳员的最少数目,所以建图后用Bellman_Ford算法求解单源最长路,在这过程中枚举sum,如果途中不存在环且s[24]=sum,那么就找到了一个可行解.
   将约束条件整理如下:
       s[i]-s[i-1]>=0      
       s[i-1]-s[i]>=-t[i]              i=(1,2……,24)
       s[24]-s[0]>=sum
       s[i]-s[j]>=r[i]          i>j    i=(j-1+8)%24+1
       s[i]-s[j]>=r[i]-sum   i<j    i=(j-1+8)%24+1

 然后根据以上约束条件建图套spfa就是了。经典题,多回来看感受约束条件的寻找方法。

 

 

你可能感兴趣的:(c,工作,算法,ini)