差分约束系统

差分约束系统用于解决N元一次不等式组。

之前感觉学的模模糊糊,现在理得比较清楚后做一个总结。


1.不等式怎么转换

先是a-b ≤ c

对于最短路,有这样的不等式:dis(u) ≤ dis(v) + val(v,u)

变形得:dis(u) - dis(v) ≤ val(v,u),与a-b ≤ c很相似

那么对于形如a-b ≤ c的不等式,我们可以从点b向点a连接一条长度为c的边。


 2.跑最长路还是最短路

这与建图的方式有关。

a-b ≤ c--->从点b向点a连接一条长度为c的边跑最短路。

 也可以是 --->从点a向点b连接一条长度为-c的边跑最长路。

如果求的是两个变量差的最大值,那么需要将所有不等式转变成"<=",建图后求最短路。(即求所有满足条件里的最小的)。

如果求的是两个变量差的最小值,那么需要将所有不等式转化成">=",建图后求最长路。(即要求所有的最小值都满足)。

而最短路和最长路是可以通过不等式移项相互转化的。


特殊连边

我们在建图的时候,并不是只有连和不等式转化的边,还有一些特殊的“隐藏边”。

可能是某个点有一定的限制。

比如一个点的位置只能填1或0,处理成前缀和以后就会有0<=sum[i] - sum[i-1]<=1。

或者说这个点有权值,为了维护这个权值,我们建一个0点,互相连边。

更多的还要多做不同的题。


 无解情况

参考

如果是求解最长路(最小值)的无解情况:

可考虑先转化成最短路(或者判个正环?)。


例题

[USACO05DEC] 布局

很明显的差分约束,连边最大值跑最短路即可。

然后要判断无解情况。

这里要建一个虚拟0点,因为不是点与点之间都有联系,从1跑不一定都跑得到。

#include<bits/stdc++.h>
#define LL long long
#define INF 2100000000
#define N 1003
#define M 10003
#define re register
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
void print(int x)
{
    if(x<0)x=-x,putchar('-');
    if(x>9)print(x/10);
    putchar(x%10+'0');
}
struct EDGE{
    int nextt,to,val;
}w[M*3];
int head[N],tot=0,n;
int vis[N],dis[N],cnt[N];
void add(int x,int y,int z)
{
    tot++;
    w[tot].nextt=head[x];
    w[tot].to=y;
    w[tot].val=z;
    head[x]=tot;
}
queue<int>q;
int spfa(int s)
{
    while(!q.empty())q.pop();
    for(int i=0;i<=n;++i)dis[i]=INF,vis[i]=0,cnt[i]=0;
    dis[s]=0;vis[s]=1;
    q.push(s);cnt[s]++;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        vis[x]=0;
        for(int i=head[x];i;i=w[i].nextt)
        {
            int v=w[i].to;
            if(dis[v]>dis[x]+w[i].val)
            {
                dis[v]=dis[x]+w[i].val;
                if(!vis[v])
                {
                    vis[v]=1,q.push(v);
                    cnt[v]++;
                    if(cnt[v]>n)return -1;
                }
            }
        }        
    }
    if(dis[n]==INF)return -2;
    else return dis[n];
}
int main()
{
//    freopen("a.in","r",stdin);
//    freopen("a.out","w",stdout);
    n=read();
    int ml=read(),md=read();
    for(re int i=1;i<=ml;++i)
    {
        int a=read();
        int b=read();//sum[b]-sum[a]<=d
        int d=read();//sum[b]<=sum[a]+d
        if(a>b)swap(a,b);
        add(a,b,d);
    }    
    for(re int i=1;i<=md;++i)
    {
        int a=read();
        int b=read();//sum[b]-sum[a]>=d
        int d=read();//sum[b]>=sum[a]+d
        if(a>b)swap(a,b);//sum[a]<=sum[b]-d
        add(b,a,-d);
    }    
    for(int i=2;i<=n;++i)//0<=sum[i]//sum[i-1]<=sum[i]+0
      add(i,i-1,0);
    for(int i=1;i<=n;++i)add(0,i,0);
    if(spfa(0)==-1){printf("-1
");return 0;}
    printf("%d
",spfa(1));
}
/*
*/
布局

倍杀测量者

一开始二分答案mid,考虑check。

这道题有一个小小的转化。因为是倍数关系,不等式里是乘除而不是加减。

于是为了转化成加减的形式,我们把倍数取log,因为log函数满足单调性。

假如我们使得每个人不穿女装,维护这样的不等式,

如果出现环则无解即有人穿女装,每次有环即可。

然后因为已知一些选手的分数,我们要去维护这些分数,

所以建一个虚拟0点,0点的分数为1,这些点与0点的关系也建到图中。

#include<bits/stdc++.h>
#define LL long long
#define INF 2100000000
#define N 1003
#define M 10003
#define re register
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
void print(int x)
{
    if(x<0)x=-x,putchar('-');
    if(x>9)print(x/10);
    putchar(x%10+'0');
}
struct EDGE{
    int nextt,to;
    double val;
}w[M*3];
int head[N],tot=0;
double dis[N];
int vis[N],cnt[N];
int o[N],a[N],b[N],k[N],sco[N],id[N];    
void add(int x,int y,double z)
{
    tot++;
    w[tot].nextt=head[x];
    w[tot].to=y;
    w[tot].val=z;
    head[x]=tot;
}
int n,s,t;
queue<int>q;
bool spfa(int S)
{
    while(!q.empty())q.pop();
    for(int i=0;i<=n;++i)dis[i]=-INF,vis[i]=0,cnt[i]=0;
    dis[S]=0;vis[S]=1;cnt[S]++;
    q.push(S);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        vis[x]=0;
        for(int i=head[x];i;i=w[i].nextt)
        {
            int v=w[i].to;
            if(dis[v]<dis[x]+w[i].val)
            {
                dis[v]=dis[x]+w[i].val;
                if(!vis[v])
                {
                    vis[v]=1;cnt[v]++;
                    if(cnt[v]>n)return 1;
                    q.push(v);
                }
            }
        }
    }
    return 0;
}
bool check(double mid)
{
    tot=0;
    memset(head,0,sizeof(head));
    for(int i=1;i<=s;++i)
    {
        if(o[i]==1)//a[i]>b[i]*(k[i]-mid)
          add(b[i],a[i],log2(k[i]-mid));
        else//b[i]/(k[i]+mid)<a[i]
          add(b[i],a[i],-log2(k[i]+mid));
    }
    for(int i=1;i<=t;++i)
    {
        add(0,id[i],log2(sco[i]));
        add(id[i],0,-log2(sco[i]));
    }
    if(spfa(0))return 1;
    return 0;
}
int main()
{
    n=read(),s=read(),t=read();
    for(int i=1;i<=s;++i)
      o[i]=read(),a[i]=read(),b[i]=read(),k[i]=read();
    for(int i=1;i<=t;++i)id[i]=read(),sco[i]=read();
    double l=0,r=10;
    double eps=1e-6;
    while(r-l>eps)
    {
        double mid=(l+r)/2.0;
        if(check(mid))l=mid;
        else r=mid;
    }
    if(l>eps)
    printf("%lf
",l);
    else printf("-1
");
}
倍杀测量者
原文地址:https://www.cnblogs.com/yyys-/p/11647183.html