差分约束系统解析

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

差分约束系统poj1201 ,poj1275的解题报告
现在发现刘汝佳写的书真的是给高手看的!。

要是一个半懂的人去看这本书根本不知所云。

简直就是晦涩难懂。

但是一旦把问题搞懂了再去看,就发现居然这个书全讲的是重点。

看来这本书只适合于做指导用。

我基本上没怎么看懂这个书。

这几天做差分系统的题的时候,经常碰到这种情况:
为什么题目明明要求的是求某某值的最小值。

但找的资料上却说的用最长路求法。

还有的地方要求某函数值的最大值,但用的方法却是求最短路的方法。

到底是求最短路还是求最长路?
最终我发现了。

只要是能用bellman_ford解决的差分约束系统,既可以用最长路求法求得,也可以用最短路求得。

并且部分可以用Dijkstra解决!
其实个人觉得用“单原点最短路,单原点最长路求法”这个两个说法来描述用bellman_ford 解决差分约束系统是不准确的。

在一般的最短路径求法中对应的松弛操作为:
If dist[b]》dist[a]+ w[a][b] then
dist[b]= dist[a]+ w[a][b]。

(1)
然而在所谓的最长路求法中松弛操作变为了
If dist[b]《dist[a]+ w[a][b] then
dist[b]= dist[a]+ w[a][b]。

(2)
也就是说,最短路就是对应(1)号松弛方法,最长路对应(2)号而已。

现在先来看看一般的例子:
假如有如下不等式组:(即求出来的最终答案要保证下列不等式成立)
s[bi] -s[ai]>= ci; 0<=ai,bi<= max; i=1,2,3,。

现在求s[max]的最小值.
用求最长路的方法:即用(2)号松弛方法
先将不等式变形:s[bi]>=s[ai]+ci;
即保证s[bi] 不小于s[ai]+ci;
而(2)号松弛操作的作用也是这个。

即保证dist[b]不小于dist[a]+ w[a][b]
于是这个个不等式便与这种松弛操作统一了。

所以就设a到b的路径为ci。

应用(2)号松弛操作得到的最后答案,就能保证不等式始终是成立的。

因此对所有s[bi] -s[ai]>= ci; 设ai到bi的距离为ci,其余没有路径的地方设为—0000(无穷小)原点到自己的距离设为0;
于是bi被不断跟新。

不断变大。

注意这个时候s[bi]是从负无穷小慢慢升上来的。

到最后计算完成后,事实上对于某些不等式有s[bi] > s[ai]+ci。

但有一条最终路径(即最长路径)上的节点是完全符合s[bi] = s[ai]+ci的
所以这个时候求到的s[max]是所有可行解中的最小值。

下面改用(1)号松弛方法。

(1)号的作用就是保证dist[b]不大于dist[a]+ w[a][b]
将不等式组变形:s[ai]-s[bi]<=-ci;再变:s[ai]<=s[bi] + (—ci)
于是(1)号松弛操作也与不等式统一了。

即我们设bi 到ai 的路径为(-ci)
用一号松弛法的算法,求最短路径。

因为是求最短路径,对于没有路径的地方
设为00(无穷大);
当算法进行时:于是s[ai]的值被不断跟新,不断缩小。

最终到达的最短路径的。

所以s[max]是所有可行解中的最小值!只不过这个时候求出的最小值与刚才求得那个最小值为相反数:这是因为:
差分约束系统构成的图一般是有向图。

求最长路(即2号松弛)是求原点到终点的距离。

而我们设原点到自己的路径为0.所以求出来的最长路径为正。

而如果反过来求最短路,画下图的话就会明白。

求的是终点到原点的距离。

如果原点自己到自己为0的话,且这些距离都为负。

所以求出来的结果是负的。

反个号就是答案了。

至此,对于同一个差分约束系统所谓的求最短路,求最长路都是可以行得通的,只不过看你怎么变化不等式。

从而最终选择相应的松弛操作而已。

假如题目再变一下。

求s[max]的最大值呢?。

如果一定要用(1)号松弛法(即求最长路),当然前提是不等式有对s[max]进行约束。

用同样的方法,同样的不等式求出来的最大值,最小值难道是一样的吗。

注意,因为差分约束图是有向图。

如果求最大值的话,则不等式
s[bi] -s[ai]>= ci; 中有很大一部分是bi<ai 且ci>0。

也就是说这个求得最大值是从终点到原点的最长路径!而不是原点到终点的最长路径!。

这两种方向都不一样。

因此。

即使是一样的图。

但求出来的东西不一样。

最后,不等式必须是大于等于,或者小于等于号。

才能正常运行。

因为我们求出来的最终某条路径,其路径上的所有节点是完全符合= 关系的。

只是对于其他路径才符合不等关系
下面举例说明:poj1201
题目分析:给出N个区间以及每个区间对应的整数,计算一个集合,使得对于每个给出的区间,集合与它的相同元素个数不小于相应的整数。

那么这个集合最小为多少?
输入:N行,每行三个整数:A B C,代表区间的两个端点,以及相应整数(最小共同元素数)
对于最长路径求法有不等式:s[i]表示0.。

I-1.已有多少个整数了。

s[b+1]-s[a]>=c;
将所有点排序后得一次将区间端点排序
s[i]-s[i-1]>= 0;
s[i-1]-s[i]>=-val(s[i-1]-s[i]) 。

V al(s[i-1]-s[i])代表两个端点的差值
于是图就建好了,同时注意约束s[max]的是它前面那个点。

也就是说求的是原点到s[max]的最长路径。

同时设原点自己到自己为0.最后求得s[max]就是答案了。

#include <iostream>
using namespace std;
const int MAX_M=50000+2;
struct Map{
int a ,b,lon;
};
Map map[6*MAX_M];
int hash[MAX_M];
int cmp( const void *p ,const void *q)
{
return ((Map *)p)->a - ((Map *)q)->a;
}
void spfa(int &ans){
int order[MAX_M],dist[MAX_M],head,last,ict,tmp,a,b,c; memset(order,1<<5,sizeof(order));
memset(dist,1<<7,sizeof(dist));
dist[0]=0;
head=0;
last=0;
while(head<0){
ict=hash[head];
while(map[ict].a==head){
a=map[ict].a;b=map[ict].b;c=map[ict].lon;
if(dist[b]<dist[a]+c){
dist[b]=dist[a]+c;
if(order[b]>0){
order[last]=b;
last=b;
}
}
ict++;
}
tmp=head;
head=order[head];
order[tmp]=0;
}
ans=dist[ans];
}
int main(){
int a,b,c,n,tot,point,ict,max;
freopen("d:/","r",stdin);
while(scanf("%d",&n)!=EOF){
tot=-1;
max=0;
memset(hash,0,sizeof(hash));
for(ict=0; ict<n; ict++){
scanf("%d%d%d",&a,&b,&c);
map[++tot].a=a+1;
map[tot].b=b+2;
map[tot].lon=c;
hash[a+1]=1;
hash[b+2]=1;
if(b+2>max)
max=b+2;
}
point=0;
for(ict=1; ict<=max; ict++){
if(hash[ict]){
map[++tot].a=point;
map[tot].b=ict;
map[tot].lon=0;
map[++tot].a=ict;
map[tot].b=point;
map[tot].lon=point-ict;
point=ict;
}
}
tot++;
qsort(map,tot,sizeof(map[0]),cmp);
hash[0]=0;
point=0;
for(ict=1; ict<tot; ict++){
if(map[ict].a==point)
continue;
hash[map[ict].a]=ict;
point=map[ict].a;
}
spfa(max);
printf("%d\n",max);
}
return 1;
}
现在用最短路径求法求:
不等数组变为
s[a]-s[b+1]<=-c;
将所有点排序后得一次将区间端点排序
s[i-1]-s[i]<= 0;
s[i]-s[i-1]<= val(s[i-1]-s[i]) 。

Val(s[i]-s[i-1])代表两个端点的差值
这个时候就是求终点s[max]到原点的距离了。

假如原点自己到自己设为0.那么最终求出来s[max]就是负数。

反号就是答案了。

注意两个程序在进行松弛的时候端点是不同的。

#include <iostream>
using namespace std;
const int MAX_M=50000+2;
struct Map{
int a ,b,lon;
};
Map map[6*MAX_M];
int hash[MAX_M];
int cmp( const void *p ,const void *q){
return ((Map *)p)->b - ((Map *)q)->b;
}
void spfa(int &ans){
int order[MAX_M],dist[MAX_M],head,last,ict,tmp,a,b,c; memset(order,1<<5,sizeof(order));
memset(dist,1<<6,sizeof(dist));
dist[0]=0;
head=0;
last=0;
while(head<0){
ict=hash[head];==head){
a=map[ict].a;b=map[ict].b;c=map[ict].lon;
if(dist[a]>dist[b]+c){
dist[a]=dist[b]+c;
if(order[a]>0){
order[last]=a;=b+2;
map[tot].b=a+1;
map[tot].lon=-c;
hash[a+1]=1;
hash[b+2]=1;
if(b+2>max)
max=b+2;
}
point=0;
for(ict=1; ict<=max; ict++){
if(hash[ict]){
map[++tot].a=point;
map[tot].b=ict;
map[tot].lon=ict-point;
map[++tot].a=ict;
map[tot].b=point;
map[tot].lon=0;
point=ict;
}
}
tot++;
qsort(map,tot,sizeof(map[0]),cmp);
hash[0]=0;
point=0;
for(ict=1; ict<tot; ict++){
if(map[ict].b==point)
continue;
hash[map[ict].b]=ict;
point=map[ict].b;
}//哈希邻接链表
spfa(max);
printf("%d\n",-max); }
return 1;
}。

相关文档
最新文档