做的第一道差分约束的题目,思考了一天,终于把差分约束弄懂了O(∩_∩)O哈哈~
题意(略坑):三元组{ai,bi,ci},表示区间[ai,bi]上至少要有ci个数字相同,其实就是说,在区间[0,50000]上,每一个三元组表示[ai,bi]之间至少要标记ci个数字,问至少要标记多少个数字。
在学习差分约束的童鞋,建议看一下:09年姜碧野的《SPFA算法的优化及应用》,06年冯威的《浅析差分约束系统》,不过后者看起来较难搞懂,也可以看http://ycool.com/post/m2uybbf 写的很不错。
这里写一下我自己的一点心得:
1、差分约束求的是什么?
这里分为两种:求值,判环。
求值:如这道题所示,最终求的是整个区间上被标记点的最小数量,也就是从起点到终点的最小值,不需考虑判环的情况。更广泛的讲,它可以求解不等式的一组解。
判环:poj 1364判断存不存在,即判正负环。
2、差分约束中的松弛操作
举个最短路的例子,1->2 c=3,1->3 c=1,3->2 c=1,即以1为起点,d[2]>d[3]+w(3,2),可以进行松弛。而三条边本身表示的是:a(2)-a(1)<=3,a(3)-a(1)<=1,a(2)-a(3)<=1,我们是从d[]=INF一路更新过来的,用1->3->2这条边更新1->2,实际上表示的是后两个不等式之和a(2)-a(1)<=2比第一个不等式的约束条件更强。
3、最长路与最短路的区别
两者不仅是在三角不等式的表现形式上不同,具体求解的值也不同。
首先要明确,需要额外的一个限定条件:一般为加入源点s,并建立权值为0的边。对于一个不等式组来说,只要有一组解,那么同时加上定值k,仍然满足约束条件。
最短路:d[v]<d[u]+w(u,v),d[i]初始化为INF,而求解出来的是不等式的最大值。何为最大值?若确定其中一个的值,并非能得到每个数的定值,因为是不等式,所以每个值有其取值范围。这里的最大值就是在源点s的限定条件内,所能够取值的上界。仔细思考,这里的最短路只是满足不等式组条件所找到的解,由于最短路是由大到小做松弛操作,找到的最短路即为“最大”。
最长路:d[v]>d[u]+w(u,v),d[i]初始化为-INF,解释同上。这道题目求的是最小值,自然就只能由最长路求解。
4、关于点的数量
如题目所示{ai,bi,ci},[ai,bi]上至少标记ci个点,可以转化为:s(bi)-s(ai-1)>=ci,注意是(ai-1)而非(ai),由于减1的关系,总点数+1。
在判环过程中,SPFA的判定条件为入队n次,其实质是最短路最长经过(n-1)条边,所以入队n次队列仍不为空,即可以判定存在正负环。当点的总数+1,对应的SPFA判环的条件也就变更为cnt[]>n。
5、关于图的连通性
无论是求值,还是判环,原图不构成强连通的前提下都是不能搜索到每一个点的,所以才出现设立一个源点s的方法。事实上,能够到达各个点,更加实用的是在SPFA初始化时,就把所有点加入队列,并把d[i]全部初始化为0。不仅仅是解决了连通的问题,更是避免了错误——加上一个源点s,使总点数为(n+2),SPFA的判环条件必须为cnt[]>(n+1),这是很容易忽视的问题。
这道题由于存在所谓的隐藏条件0<=s(i)-s(i-1)<=1,据此建图已经能够实现图上的连通。
6、不等式的变形
差分约束解决的是>=和<=的问题,若题目给出的是>k(或<k),需要变形。比如若k是整数,>k等价于>=k+1。
7、无穷解,无解。
http://www.cnblogs.com/zstu-abc/archive/2013/08/18.html
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<algorithm> 5 #define rep(i,a,b) for(int i=a;i<=b;i++) 6 #define clr(a,m) memset(a,m,sizeof(a)) 7 using namespace std; 8 9 const int MAXN=55555; 10 const int INF =MAXN; 11 12 struct Edge{ 13 int v,c,next; 14 Edge(){} 15 Edge(int _v,int _c,int _next):v(_v),c(_c),next(_next){} 16 }edge[MAXN<<2]; 17 18 int head[MAXN],tol; 19 int d[MAXN],inq[MAXN]; 20 21 void init() 22 { 23 tol=0; 24 clr(head,-1); 25 } 26 27 void add(int u,int v,int c) 28 { 29 edge[tol]=Edge(v,c,head[u]); 30 head[u]=tol++; 31 } 32 33 void SPFA(int down,int up) 34 { 35 queue<int>q; 36 clr(inq,0); 37 rep(i,down,up) 38 if(i==down)d[i]=0; 39 else d[i]=-INF; 40 q.push(down); 41 inq[down]=true; 42 while(!q.empty()) 43 { 44 int u=q.front();q.pop(); 45 inq[u]=false; 46 for(int i=head[u];i!=-1;i=edge[i].next) 47 { 48 int v=edge[i].v; 49 int c=edge[i].c; 50 if(d[v]<d[u]+c){ 51 d[v]=d[u]+c; 52 if(!inq[v]){ 53 q.push(v); 54 inq[v]=true; 55 } 56 } 57 } 58 } 59 } 60 61 int main() 62 { 63 int n,up=0,down=MAXN; 64 int u,v,c; 65 scanf("%d",&n); 66 67 init(); 68 rep(i,1,n){ 69 scanf("%d%d%d",&u,&v,&c); 70 add(--u,v,c); 71 up=max(up,v); 72 down=min(down,u); 73 } 74 rep(i,down,up){ 75 add(i-1,i,0); 76 add(i,i-1,-1); 77 } 78 SPFA(down,up); 79 80 printf("%d ",d[up]); 81 return 0; 82 }