图论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\n",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;
}

 

  

 

posted @ 2020-03-31 20:47  CZD648  阅读(202)  评论(0编辑  收藏  举报
Live2D