嗯,这两天用空闲时间把差分约束系统学习了一下。话说暑假已经快结束了,在忙碌的时候,依然悠哉悠哉地学算法真的好吗。。。算了,学都已经学了,就把它放到网上作纪念吧(才不是强迫症呢,哼!)。因为是刚学的,所以有很多地方可能理解不当,希望大神们多加指教。
差分约束系统,就是将不等式的约束问题,抽象成最短路问题的一种方法,属于线性规划的一种。所以在此之前至少要熟练掌握Bellman-ford算法或者spfa算法的其中一种。Dijkstra是不行的,因为要处理负权边的问题。
首先我们先看看如何把不等式的约束问题转化为图论的最短路问题。假设有这样的约束条件:S[3]-S[0]<=k1, S[5]-S[3]<=k2,那么两式相加,则可以得出S[5]-S[0]<=k1+k2。可以抽象成,从节点0到3的距离为k1,从节点3到节点5的距离为k2.因为要满足所有的约束条件,对于<=这个关系,我们只需要求出0到5的最短路,最短路的长度,就是S[0]-S[5]的最大值。如果是>=这个关系,则需要求出最长路,最长路的长度,就是S[5]-S[0]的最小值。对于任意的S[i]-S[j]<=ki也可以根据上述条件很轻松地建图。如果不等式的关系不一致怎么办?比如S[i]-S[j]>=k1, S[k]-S[j]<=k2.那么可以对于其中的一个不等式,两边同时乘以-1,可以得到S[j]-S[k]>=-k2,然后建图求解。
另外,不等式除了有一个解的情况,还有无数个解的情况和无解的情况,这又该如何判断呢?这里,在下就直接说方法了,不加证明了(拿特殊值代入法也能帮助理解)。当图中存在负环的时候,无解。当最后一个节点没有被更新的时候,有无穷多个解。
差分约束系统介绍完了,下面我们来看例题hdu1531.
hdu1531这一题,赤裸裸的差分约束系统的题目。就是国王给出一堆不等式,然后判断这个不等式所满足的数列是否存在,如果存在则输出lamentable king,如果不存在,则输出successful conspiracy。好的,下面来说明这道题。
因为这道题没有让你输出解,只让你输出是否有解,题目就变得更加简单了。设那个数列a1+a2+a3+...+an=Sn可以获得题目上的条件有两种可能,①S[si+ni]-S[si-1]>ki,②S[si+ni]-S[si-1]<ki。因为是<和>符号,我们需要把它换成>=和<=,怎么办呢?直接对于ki操作即可,变成>=ki+1,<=ki-1。最后变形完成可得:①S[si-1]-S[si+ni]<=-ki-1,②S[si+ni]-S[si-1]<=ki-1。然后直接建图,从0到n过一遍spfa或者bellman-ford就可以了,如果有负环,则输出successful conspiracy,如果没有则输出lamentable king。但是需要注意的是,要使整个图联通才可以,所以将0节点设为虚点,我们在这里叫他超级节点。这个节点与所有点连通,权值为小于dist的初值的无穷大。我这里做的是求最短路。还有一点需要注意的是,因为是从0到n所以有n+1个节点,所以入队次数大于n+1时,才能被判断是负环,也算是一个小细节了,下面上代码:
代码君:
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
typedef struct
{
int x,y,z;
}atp;
atp line[220];
int s[110],nn[110],kk[110],found[110],dist[110],hash[110],flag,n,m,d[5000];
char o[110][110];
bool cmp(atp a,atp b)
{
return (a.x<b.x);
}
void spfa()
{
int f,r,vis[110],i;
f=1;r=1;
memset(vis,0,sizeof(vis));
d[f]=0; dist[0]=0; vis[0]=1;
while(f<=r)
{
for(i=found[d[f]];line[i].x==d[f]&&i<=m+n;i++)
{
if(dist[line[i].y]>dist[line[i].x]+line[i].z)
{
dist[line[i].y]=dist[line[i].x]+line[i].z;
if(!vis[line[i].y])
{
if(hash[line[i].y]>n+1) //要注意是>n+1而不是n
{
flag=1;
return;
}
r++;
vis[line[i].y]=1;
d[r]=line[i].y;
hash[line[i].y]++;
}
}
}
vis[d[f]]=0;
f++;
}
}
int main()
{
int i,j,k;
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
scanf("%d",&m);
for(i=1;i<=m;i++)
scanf("%d%d%s%d",&s[i],&nn[i],o[i],&kk[i]);
k=0;
for(i=m+1;i<=m+n+1;i++)
{
line[i].x=0;
k++;
line[i].y=k;
line[i].z=10000000;
}
for(i=1;i<=m;i++)
{
if(strcmp(o[i],"gt")==0)
{
line[i].x=s[i]+nn[i];
line[i].y=s[i]-1;
line[i].z=-kk[i]-1;
}
if(strcmp(o[i],"lt")==0)
{
line[i].x=s[i]-1;
line[i].y=s[i]+nn[i];
line[i].z=kk[i]-1;
}
}
k=1;
for(i=0;i<=100;i++)
dist[i]=2147483647;
sort(line+1,line+m+n+1,cmp);
line[0].x=-1;
for(i=1;i<=m+n;i++)
{
if(line[i].x!=line[i-1].x)
found[line[i].x]=i;
}
memset(hash,0,sizeof(hash));
flag=0;
spfa();
if(flag==1)
printf("successful conspiracy\n");
else
printf("lamentable kingdom\n");
}
return 0;
}