P1993 小K的农场(差分约束)

题目描述

小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共m个),以下列三种形式描述:

  • 农场a比农场b至少多种植了c个单位的作物,
  • 农场a比农场b至多多种植了c个单位的作物,
  • 农场a与农场b种植的作物数一样多。

但是,由于小K的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。

输入输出格式

输入格式:

第一行包括两个整数 n 和 m,分别表示农场数目和小 K 记忆中的信息数目。

接下来 m 行:

如果每行的第一个数是 1,接下来有 3 个整数 a,b,c,表示农场 a 比农场 b 至少多种植了 c 个单位的作物。

如果每行的第一个数是 2,接下来有 3 个整数 a,b,c,表示农场 a 比农场 b 至多多种植了 c 个单位的作物。如果每行的第一个数是 3,接下来有 2 个整数 a,b,表示农场 a 种植的的数量和 b 一样多。

输出格式:

如果存在某种情况与小 K 的记忆吻合,输出“Yes”,否则输出“No”。

先说一下什么是差分约束:

在图上用x->y的边权c表示v(x)-v(y)>=c(或小于等于), 对于多个不等式组:

v(x1) - v(x2) >= c1

v(x2) - v(x3) >= c2

……

把它们加起来就会得到:v(x1) - v(xn) >= c1 + c2 + …… + cn-1;实际上这些不等式组可以看成一个路径,每个不等式即路径上的一小段,毕竟路径总长可以看作每段路径之和。

所以说,按照这种方式建图,就可以包含任意约束条件相加的结果。

这道题思路:

输入的三个不等式:a,b,c -> a-b>=c,连a->b权值为c的边 ,a,b,c -> a-b<=c,即b-a>=-c,连b->a权值为-c的边 ,a,b -> a-b>=0,且b-a>=0,即在a,b间接双向边,边权都为0。再设置一个超级源点0,因为图可能不连通,所以要开大点的数组。

#include 
#define INF 2333333
#define mem(ar,num) memset(ar,num,sizeof(ar))
#define me(ar) memset(ar,0,sizeof(ar))
#define lowbit(x) (x&(-x))
#define IOS ios::sync_with_stdio(false)
#define DEBUG cout<>n>>m;
    while(m--){
        cin>>a>>b>>c;
        if(a==1)
            cin>>d;add(c,b,d);
        else if(a==2)
            cin>>d;add(b,c,-d);
        else if(a==3)
            add(b,c,0),add(c,b,0);
    }
    for(int i=1;i<=n;i++)
        add(0,i,0),dis[i]=-INF;
    if(!dfs(0))cout<<"No";
    else cout<<"Yes";
    return 0;
}

还有一种类似于BFS的spfa,不过会TLE:

#include 
#define INF 0x3f3f3f3f
#define mem(ar,num) memset(ar,num,sizeof(ar))
#define me(ar) memset(ar,0,sizeof(ar))
#define lowbit(x) (x&(-x))
#define IOS ios::sync_with_stdio(false)
#define DEBuG cout<q;
    q.push(u);
    vis[u]=1;
    cc[u]=1;
    while(!q.empty()){
        int s=q.front();q.pop();
        vis[s]=0;
        if(++cc[s]>n)return 0;//若存在负环,返回false
        for(int i=h[s];i;i=no[i].net){
            if(dis[no[i].to]>dis[s]+no[i].w){
                int j=no[i].to;
                dis[j]=dis[s]+no[i].w;
                if(!vis[j]){
                    vis[j]=1;
                    q.push(j);
                }
            }
        }
    }
    return 1;
}
int main(){IOS;
    cin>>n>>ml;
    for(int i=0;i>p>>a>>b;
        if(p==3)
        {
            add(a,b,0);
            add(b,a,0);
            continue ;
        }
        cin>>c;
        if(p==2)
            add(a,b,c);
        else
            add(b,a,-c);
    }
    for(int i=1;i<=n;i++){
        dis[i]=INF;
        add(0,i,0);
    }
    if(spfa(0))cout<<"Yes";
    else cout<<"No";
    return 0;
}

双端队列优化:

#include 
#define INF 0x3f3f3f3f
#define mem(ar,num) memset(ar,num,sizeof(ar))
#define me(ar) memset(ar,0,sizeof(ar))
#define lowbit(x) (x&(-x))
#define IOS ios::sync_with_stdio(false)
#define DEBuG cout<q;
    q.push_front(u);
    vis[u]=1;
    cc[u]=1;
    while(!q.empty()){
        int s=q.front();q.pop_front();
        vis[s]=0;
        for(int i=h[s];i;i=no[i].net){
            if(dis[no[i].to]>dis[s]+no[i].w){
                int j=no[i].to;
                dis[j]=dis[s]+no[i].w;
                cc[j]=cc[s]+1;//这个要注意
                if(cc[j]>n)return 0;//若存在负环,返回false
                if(!vis[j]){
                    if(!q.empty() && dis[j]>dis[q.front()])//双端队列优化SPFA如果这个点的dis值大于第一个点的dis值,就普通的将这个点假如队列尾,如果不大于,就将它作为队列的第一个点。这样优化大约能快三分之一
                        q.push_back(j);
                    else
                        q.push_front(j);
                    vis[j]=1;
                }
            }
        }
    }
    return 1;
}
int main(){IOS;
    cin>>n>>ml;
    for(int i=0;i>p>>a>>b;
        if(p==3)
        {
            add(a,b,0);
            add(b,a,0);
            continue ;
        }
        cin>>c;
        if(p==2)
            add(a,b,c);
        else
            add(b,a,-c);
    }
    for(int i=1;i<=n;i++){
        add(0,i,0);
        dis[i]=INF;
    }
    if(spfa(0))cout<<"Yes";
    else cout<<"No";
    return 0;
}

 

你可能感兴趣的:(差分约束)