差分约束学习笔记
差分约束(Difference-Constraints)
引出差分约束
当我们遇到一个不等式组,比如下面这个
怎么进行求解?
我们可以很容易地想到:
给其中一个变量赋一个具体的值,然后再逐渐估测其他变量的值,并不断修改。
比如:
1.先给
2.根据
3.发现
4.解得,
然鹅,这样的方法只适合于规模较小的时候,而且是在猜的基础上的,这样也没有办法来解出题目。
我们观察下这些不等式,像在最短路里的三角不等式,于是,发现了这些性质:
我们设
所以,我们只要对于每个不等式
用
这个是模板。
int dis[5005],cnt[5005];
bool vis[5005];
queue<int>q;
bool SPFA(int s)
{
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=0;
for(int i=fir[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(dis[v]>dis[u]+e[i].val)
{
dis[v]=dis[u]+e[i].val;
cnt[v]++;
if(cnt[v]==n)
return 0;
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
}
}
}
}
return 1;
}
寻找可行解
例题
因此对于这类不等式组的求解,我们可以将其抽象成一个有
补充:
如果存在符号相反的不等式,如
建完图后,为了每个点的可达性,我们要新建一个超级源点
于是就很容易地做出来这模板题了。
#include<iostream>
#include<cstdio>
#include<string.h>
#include<queue>
using namespace std;
int n,m,fir[5005],tot;
struct edge
{
int nxt,to,val;
}e[10005];
void add(int u,int v,int w)
{
e[++tot]={fir[u],v,w};fir[u]=tot;
}
SPFA
int main()
{
scanf("%d%d",&n,&m);
int s=n+1;
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(v,u,w);
}
for(int i=1;i<=n;i++)
add(s,i,0);
memset(dis,0x3f,sizeof dis);
if(!SPFA(s))
{
cout<<"NO";
return 0;
}
for(int i=1;i<=n;i++)
cout<<dis[i]<<" ";
return 0;
}
寻找最大解/最小解
最大解:
就是上面那个求法,所以我不再多加述
最小解:
要使所有的
与上面的类似
补充:
如果存在符号相反的不等式,如
注意:求解最小解的图中,因为不存在正环,所以存在最长路。
无解/无穷解
无解:
比如当同时出现
- 求最短路时出现负环
- 求最长路时出现正环
代码之前已经放过了
无穷解
当所取源点无法到达某一点
比如当同时出现
例题
P3275 [SCOI2011]糖果
记小朋友
则题目求的就是满足这些不等式,且所有
#include<iostream>
#include<cstdio>
#include<queue>
#include<string.h>
using namespace std;
int n,k,dis[300005],cnt[300005];
int fir[300005],tot;
long long ans;
struct edge
{
int nxt,to,val;
}e[300005];
int read()
{
int a=0;
char x=getchar();
while(x<'0'||x>'9') x=getchar();
while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+x-48,x=getchar();
return a;
}
void add(int u,int v,int w)
{
e[++tot]={fir[u],v,w};fir[u]=tot;
}
bool vis[300005],flag;
queue<int>q;
bool SPFA(int s)
{
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=0;
for(int i=fir[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(dis[v]<dis[u]+e[i].val)
{
dis[v]=dis[u]+e[i].val;
cnt[v]++;
if(cnt[v]>=n)
{
cout<<-1;
return 0;
}
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
}
}
}
}
return 1;
}
int main()
{
n=read();k=read();
int s=n+1;
while(k--)
{
int opt,u,v;
opt=read();u=read();v=read();
if(opt==1) add(u,v,0),add(v,u,0);
else if(opt==2) add(u,v,1);
else if(opt==3) add(v,u,0);
else if(opt==4) add(v,u,1);
else add(u,v,0);
if((u==v)&&(opt==2||opt==4))
{
cout<<-1;
return 0;
}
}
for(int i=n;i>=1;i--) add(s,i,1);
if(!SPFA(s)) return 0;
for(int i=1;i<=n;i++)
ans+=dis[i];
cout<<ans;
return 0;
}
注意:
加边的时候是要倒着加,从