zoukankan      html  css  js  c++  java
  • 图论7-差分约束系统

    抱歉图论两天没更了,今天更一个差分约束,这也是比较有用的一个知识点吧。

    差分约束系统是什么呢?给定一些约束条件,然后求出有/没有符合这个约束条件的对其中每个元素的取值,当然,也能算具体的值是多少。

    对了,看这篇博客之前先学会SPFA算法。一道例题:luoguP1993

    题目大意:有n个变量和一些关系条件,每一种关系条件都形如以下:a>=b+x,a<=b+x或a=b,x是个常数,问这些关系条件是否有矛盾。

    差分约束解析:差分约束中的约束条件就是和这道题的条件的形式一样,比如a>=b+x,看到这个,有没有想起SPFA中更新dis[y]是的式子

    dis[y]<=dis[x]+val[i]?如果有a<=b+x是不是就能建一个从b到a的路径,权值为x,然后跑最短路就行了。如果建边的那块没有看懂也没事

    可以看下面那张图片,清楚的解释了差分约束系统的建边。

    看完这个图之后应该就明白了差分约束的建边原理了,但是还有一类边a>=b+x怎么建呢?很好办,做一个数学转化,变成b<=a+(-x),

    是不是现在会建了?就建一条从b到a长度为-x的边就行了。但是这道题中的判断是否成立却有一点难度,怎么叫不成立呢?是不是就是

    先说a>=b+x再说a<=b+y且y<x,再看最短路中,就是先建了a->b,-x再建b->a,y,又因为y<x,所以y-x<0所以这种情况就会出现负环

    还有几种不成立都会导致负环的产生,所以可以用spfa判一波负环,如果有负环,输出NO,否则输出YES。代码如下:

    #include<bits/stdc++.h>
    #define xms cte
    #define int long long
    using namespace std;
    const int NR=10005;
    const int INF=1e18+10;
    int n,m,s;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*f;
    }
    int to[NR*3],nxt[NR*3],val[NR*3];
    int head[NR];
    int tot=1;
    void add(int x,int y,int z)
    {
        to[tot]=y;
        val[tot]=z;
        nxt[tot]=head[x];
        head[x]=tot++;
    }
    bool flag=0;
    int dis[NR],cnt[NR];
    int cnt2[NR];
    bool vis[NR];
    void SPFA()
    {
        for(int i=0;i<=n;i++) dis[i]=-1e18;
        queue<int> q;
        q.push(s);
        vis[s]=1,dis[s]=0;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
    //        cout<<x<<endl;
            cnt2[x]++;
            vis[x]=0;
            if(cnt2[x]>=n)
            {
                flag=1;
                return;
            }
            for(int i=head[x];i;i=nxt[i])
            {
                int y=to[i];
                if(dis[y]<dis[x]+val[i])
                {
                    
                    dis[y]=dis[x]+val[i];
                    if(!vis[y])
                    {
                        q.push(y);
                        vis[y]=1;
                    }
                }
            }
        }
    }
    signed main()
    {
    //    freopen("farm.in","r",stdin);
    //    freopen("farm.out","w",stdout);
        n=read(),m=read();
        for(int i=1;i<=m;i++)
        {
            int op;
            op=read();
            if(op==1)
            {
                int a=read(),b=read(),c=read();
                add(a,b,c);
            }
            if(op==2)
            {
                int a=read(),b=read(),c=read();
                add(b,a,-c);
            }
            if(op==3)
            {
                int a=read(),b=read();
                add(a,b,0);
                add(b,a,0);
            }
        }
        for(int i=1;i<=n;i++) add(0,i,0);
        SPFA();
        if(!flag) puts("Yes");
        else puts("No");
        return 0;
    }

    注意在洛谷上提交时必须开O2优化,否则洛谷卡bfs版的spfa

    然后,基础的差分约束讲完了,现在来讲一些进阶的。

    如果发现这个约束条件没有那么容易怎么办呢?下面我来推荐一道题:POJ1275

    题目大意:有一家24小时超市,每个小时最少需要a[i]个员工,有n(n<=1000)个应聘者,每个人都有一个参数x,表示从第x之后8个小时,

    这个人能工作,问招聘多少人能满足要求。如果不能满足,则输出No solution。

    题目解析:这道题如果不说是差分约束是不是很那想到啊?这道题怎么差分约束呢?首先要确定节点是什么,然后再想别的。我们用s数组

    表示前缀和,s[i]表示所有i天之前工作者的数量,易得s[i]-s[i-8]>=a[i],但当i<7时,就会涉及三元不等式,为了解决这个问题,我们可以

    枚s[23],并将i<7时变成二元不等式,然后做spfa就行了。建议大家先不看代码,自己写一写这道题。

    但我还是将代码放在这里吧

    #include<bits/stdc++.h>
    using namespace std;
    const int NR=105;
    const int MR=2e3+10;
    int n;
    int a[NR],num[NR];
    int to[MR],nxt[MR],val[MR];
    int head[NR];
    int tot=1;
    void add(int x,int y,int z)
    {
    	to[tot]=y;
    	val[tot]=z;
    	nxt[tot]=head[x];
    	head[x]=tot++;
    }
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    	return x*f;
    }
    bool vis[NR];
    int dis[NR];
    void Init()
    {
    	tot=1;
    	memset(head,0,sizeof(head));
    	memset(vis,0,sizeof(vis));
    	memset(dis,-0x3f,sizeof(dis));
    }
    void build(int x)
    {
    	for(int i=0;i<=23;i++)
    	{
    		add(i,i+1,0);
    		add(i+1,i,-num[i]);
    	}
    	for(int i=7;i<=23;i++) add(i-7,i+1,a[i]);
    	add(0,24,x),add(24,0,-x);
    	for(int i=0;i<=6;i++) add(i+17,i+1,a[i]-x);
    }
    bool check(int x)
    {
    	Init();
    	build(x);
    	queue<int> q;
    	q.push(0),dis[0]=0,vis[0]=1;
    	while(!q.empty())
    	{
    		int now=q.front();q.pop();vis[now]=0;
    		if(now==24&&dis[now]>x) return 0;
    		for(int i=head[now];i;i=nxt[i])
    		{
    			int y=to[i];
    			if(dis[y]<dis[now]+val[i])
    			{
    				dis[y]=dis[now]+val[i];
    				if(!vis[y])
    				{
    					q.push(y);
    					vis[y]=1;
    				}
    			}
    		}
    	}
    	return dis[24]==x;
    }
    void solve()
    {
    	int ans=-1;
    	for(int i=0;i<=n;i++)
    	{
    		if(check(i))
    		{
    			ans=i;
    			break;
    		}
    	}
    	if(ans<0) puts("No Solution");
    	else printf("%d
    ",ans);
    }
    int main()
    {
    //	freopen("1.in","r",stdin);
    //	freopen("1.out","w",stdout);
    	int T=read();
    	while(T--)
    	{
    		memset(num,0,sizeof(num));
    		for(int i=0;i<=23;i++)
    		{
    			a[i]=read();
    		}
    		n=read();
    		for(int i=1;i<=n;i++)
    		{
    			int x=read();
    			num[x]++;
    		}
    		solve();
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    WampServer Mysql配置
    Java实现 蓝桥杯VIP 算法提高 陶陶摘苹果2
    Java实现 蓝桥杯VIP 算法提高 陶陶摘苹果2
    Java实现 蓝桥杯VIP 算法提高 陶陶摘苹果2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 前10名
  • 原文地址:https://www.cnblogs.com/chen-1/p/12608187.html
Copyright © 2011-2022 走看看