bzoj 3436 小K的农场 差分约束系统

【题目背景】

小K是个特么喜欢玩MC的孩纸。。。

【题目描述】

小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共m个),以下列三种形式描述:农场a比农场b至少多种植了c个单位的作物,农场a比农场b至多多种植了c个单位的作物,农场a与农场b种植的作物数一样多。但是,由于小K的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。

【输入格式】

第一行包括两个整数n和m,分别表示农场数目和小K记忆中的信息的数目接下来m行:如果每行的第一个数是1,接下来有三个整数a,b,c,表示农场a比农场b至少多种植了c个单位的作物如果每行第一个数是2,接下来有三个整数a,b,c,表示农场a比农场b至多多种植了c个单位的作物如果每行第一个数是3,接下来有两个整数a,b,表示农场a种植的数量与b一样。1<=n,m,a,b,c<=10000

【输出格式】

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

【样例输入】

3 3
3 1 2
1 1 3 1
2 2 3 2

【样例输出】

Yes

【样例解释】

三个农场种植的数量可以为(2,2,1)

【题目解析】
这道题是典型的差分约束系统,差分约束系统听起来特别高大上。

如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。(来自度娘)

我们处理起来分两步:

1.建图:
农场a比农场b至少多种植了c个单位的作物,我们可以简单的写a-b>=c,那么我们add(a,b,-c),b是子节点,-c是边权,那么很好理解,a点到b点的代价是-c。我是这么理解的:a农场到b农场得减少c个农作物,这样,a农场的农作物的数量就和b农场一样了,从某种意义上讲,它就变成了b农场,到达了b农场。
农场a比农场b至多多种植了c个单位的作物,那我们简单的写a-b<=c,转换一下,b-a>=-c,那么我们add(b,a,c),理解如上。
相同就最好处理了,add(a,b,0)
2.判负环
我们用DFS版的spfa来处理
很好理解,因为spfa是求最短路,我们用DFS一路扎(遍历)下去,把一路扎过的点标记一下,那么如果我们又扎了回来,我们肯定陷入了一个环里,那么就是不合法的,相当于你不停走这个环,最短路越来越小。(DFS来判环真的方便啊!!!虽然我理解了好久qwq)

下面附上我的代码:

#include<cstdio>
#define maxn 10005
using namespace std;
bool flg,vis[maxn];
int n,e,tot,w[maxn],lnk[maxn],net[maxn],son[maxn],dis[maxn];
inline int read(){
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
inline void add(int x,int y,int z){son[++tot]=y;net[tot]=lnk[x];w[tot]=z;lnk[x]=tot;}
inline void DFS(int x){
    vis[x]=1;//把扎过点标记下。
    for (int j=lnk[x];j;j=net[j]){
        if (dis[son[j]]>dis[x]+w[j]){
            if (vis[son[j]]){flg=1;break;}//发现扎回来了,就有问题了。
            dis[son[j]]=dis[x]+w[j];
            DFS(son[j]);
        }
    }
    vis[x]=0;
}
int main(){
    //freopen("3436.in","r",stdin);
    //freopen("3436.out","w",stdout);
    n=read();e=read();
    for (int i=1;i<=e;i++){
        int knd=read();
        if (knd==1){
            int x=read(),y=read(),z=read();
            add(x,y,-z);
        }
        if (knd==2){
            int x=read(),y=read(),z=read();
            add(y,x,z);
        }
        if (knd==3){
            int x=read(),y=read();
            add(x,y,0);
        }
    }
    for (int i=1;i<=n;i++){
        dis[i]=0;DFS(i);
    }
    if (flg) printf("No\n");else printf("Yes\n");
    return 0;
}
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值