差分约束系统
Posted yyys-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了差分约束系统相关的知识,希望对你有一定的参考价值。
差分约束系统用于解决N元一次不等式组。
之前感觉学的模模糊糊,现在理得比较清楚后做一个总结。
1.不等式怎么转换
先是a-b ≤ c
对于最短路,有这样的不等式:dis(u) ≤ dis(v) + val(v,u)
变形得:dis(u) - dis(v) ≤ val(v,u),与a-b ≤ c很相似
那么对于形如a-b ≤ c的不等式,我们可以从点b向点a连接一条长度为c的边。
2.跑最长路还是最短路
这与建图的方式有关。
a-b ≤ c--->从点b向点a连接一条长度为c的边跑最短路。
也可以是 --->从点a向点b连接一条长度为-c的边跑最长路。
如果求的是两个变量差的最大值,那么需要将所有不等式转变成"<=",建图后求最短路。(即求所有满足条件里的最小的)。
如果求的是两个变量差的最小值,那么需要将所有不等式转化成">=",建图后求最长路。(即要求所有的最小值都满足)。
而最短路和最长路是可以通过不等式移项相互转化的。
特殊连边
我们在建图的时候,并不是只有连和不等式转化的边,还有一些特殊的“隐藏边”。
可能是某个点有一定的限制。
比如一个点的位置只能填1或0,处理成前缀和以后就会有0<=sum[i] - sum[i-1]<=1。
或者说这个点有权值,为了维护这个权值,我们建一个0点,互相连边。
更多的还要多做不同的题。
无解情况
如果是求解最长路(最小值)的无解情况:
可考虑先转化成最短路(或者判个正环?)。
例题
[USACO05DEC] 布局
很明显的差分约束,连边最大值跑最短路即可。
然后要判断无解情况。
这里要建一个虚拟0点,因为不是点与点之间都有联系,从1跑不一定都跑得到。
#include<bits/stdc++.h> #define LL long long #define INF 2100000000 #define N 1003 #define M 10003 #define re register using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<‘0‘||s>‘9‘){if(s==‘-‘)f=-1;s=getchar();} while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();} return x*f; } void print(int x) { if(x<0)x=-x,putchar(‘-‘); if(x>9)print(x/10); putchar(x%10+‘0‘); } struct EDGE{ int nextt,to,val; }w[M*3]; int head[N],tot=0,n; int vis[N],dis[N],cnt[N]; void add(int x,int y,int z) { tot++; w[tot].nextt=head[x]; w[tot].to=y; w[tot].val=z; head[x]=tot; } queue<int>q; int spfa(int s) { while(!q.empty())q.pop(); for(int i=0;i<=n;++i)dis[i]=INF,vis[i]=0,cnt[i]=0; dis[s]=0;vis[s]=1; q.push(s);cnt[s]++; while(!q.empty()) { int x=q.front();q.pop(); vis[x]=0; for(int i=head[x];i;i=w[i].nextt) { int v=w[i].to; if(dis[v]>dis[x]+w[i].val) { dis[v]=dis[x]+w[i].val; if(!vis[v]) { vis[v]=1,q.push(v); cnt[v]++; if(cnt[v]>n)return -1; } } } } if(dis[n]==INF)return -2; else return dis[n]; } int main() { // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); n=read(); int ml=read(),md=read(); for(re int i=1;i<=ml;++i) { int a=read(); int b=read();//sum[b]-sum[a]<=d int d=read();//sum[b]<=sum[a]+d if(a>b)swap(a,b); add(a,b,d); } for(re int i=1;i<=md;++i) { int a=read(); int b=read();//sum[b]-sum[a]>=d int d=read();//sum[b]>=sum[a]+d if(a>b)swap(a,b);//sum[a]<=sum[b]-d add(b,a,-d); } for(int i=2;i<=n;++i)//0<=sum[i]//sum[i-1]<=sum[i]+0 add(i,i-1,0); for(int i=1;i<=n;++i)add(0,i,0); if(spfa(0)==-1){printf("-1 ");return 0;} printf("%d ",spfa(1)); } /* */
倍杀测量者
一开始二分答案mid,考虑check。
这道题有一个小小的转化。因为是倍数关系,不等式里是乘除而不是加减。
于是为了转化成加减的形式,我们把倍数取log,因为log函数满足单调性。
假如我们使得每个人不穿女装,维护这样的不等式,
如果出现环则无解即有人穿女装,每次有环即可。
然后因为已知一些选手的分数,我们要去维护这些分数,
所以建一个虚拟0点,0点的分数为1,这些点与0点的关系也建到图中。
#include<bits/stdc++.h> #define LL long long #define INF 2100000000 #define N 1003 #define M 10003 #define re register using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<‘0‘||s>‘9‘){if(s==‘-‘)f=-1;s=getchar();} while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();} return x*f; } void print(int x) { if(x<0)x=-x,putchar(‘-‘); if(x>9)print(x/10); putchar(x%10+‘0‘); } struct EDGE{ int nextt,to; double val; }w[M*3]; int head[N],tot=0; double dis[N]; int vis[N],cnt[N]; int o[N],a[N],b[N],k[N],sco[N],id[N]; void add(int x,int y,double z) { tot++; w[tot].nextt=head[x]; w[tot].to=y; w[tot].val=z; head[x]=tot; } int n,s,t; queue<int>q; bool spfa(int S) { while(!q.empty())q.pop(); for(int i=0;i<=n;++i)dis[i]=-INF,vis[i]=0,cnt[i]=0; dis[S]=0;vis[S]=1;cnt[S]++; q.push(S); while(!q.empty()) { int x=q.front();q.pop(); vis[x]=0; for(int i=head[x];i;i=w[i].nextt) { int v=w[i].to; if(dis[v]<dis[x]+w[i].val) { dis[v]=dis[x]+w[i].val; if(!vis[v]) { vis[v]=1;cnt[v]++; if(cnt[v]>n)return 1; q.push(v); } } } } return 0; } bool check(double mid) { tot=0; memset(head,0,sizeof(head)); for(int i=1;i<=s;++i) { if(o[i]==1)//a[i]>b[i]*(k[i]-mid) add(b[i],a[i],log2(k[i]-mid)); else//b[i]/(k[i]+mid)<a[i] add(b[i],a[i],-log2(k[i]+mid)); } for(int i=1;i<=t;++i) { add(0,id[i],log2(sco[i])); add(id[i],0,-log2(sco[i])); } if(spfa(0))return 1; return 0; } int main() { n=read(),s=read(),t=read(); for(int i=1;i<=s;++i) o[i]=read(),a[i]=read(),b[i]=read(),k[i]=read(); for(int i=1;i<=t;++i)id[i]=read(),sco[i]=read(); double l=0,r=10; double eps=1e-6; while(r-l>eps) { double mid=(l+r)/2.0; if(check(mid))l=mid; else r=mid; } if(l>eps) printf("%lf ",l); else printf("-1 "); }
以上是关于差分约束系统的主要内容,如果未能解决你的问题,请参考以下文章