【学习】差分约束
今天%你赛考了差分约束相关,于是发现又有忘了的东西,复习
0x00 差分约束
差分约束是求解N元一次特殊不等式组的一种方法。差分约束系统包含$N$个变量和$M$个约束条件,每个约束条件都是一个关于其中两个变量的一个一次不等式
,每个不等式形如$x[i]-x[j]≤a[k]$,$x[i],x[j]$为变量,$a[k]$为常数。
引理:若${Xi}$为差分约束系统的一组解,$∆$为任意常数,那么${Xi+∆}$也是一组解。
0x10 最短路
差分约束系统中的每个不等式都与最短路中的三角形不等式$dis[v]≤dis[u]+e(u,v)$,即$dis[v]-dis[u]≤e(u,v)$形似。
所以我们把每个变量$x_i$看做有向图中的一个点i,对于每个不等式$x[i]-x[j]≤a[k]$,即$x[i]≤x[j]+a[k]$,可看做从$j$向$i$连一条有向边,边权为$a[k]$。
另设源点$S$,由$S$向所有点建边权为$a$的边,求完最短路后的$dis[i]$即为$X[i]$的一组解。
如果有负环,说明无解。
如果想求最小值,将不等式*=-1,则≤变为≥,用最长路解决即可,如果有正环则无解。
0x11 负环问题
如何判断是否出现负环?
①记录每个点的入队次数,若某个点入队超过n次说明有负环。
②记录更新到每个点的最短路长度,如果dis[x]+e(x,y)更新了y,那么len[y]=len[x]+1,若长度超过n说明有负环。
被学长推荐第二种
0x20 例题
poj 3169
Luogu P1993 小k的农场
裸的板板
code
1 #include <bits/stdc++.h> 2 using namespace std; 3 namespace gengyf{ 4 #define ll long long 5 const int inf=1e9+7; 6 const int maxn=1e5+10; 7 inline int read(){ 8 int x=0,f=1; 9 char c=getchar(); 10 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 11 while(c>='0'&&c<='9'){x=(x*10)+c-'0';c=getchar();} 12 return x*f; 13 } 14 struct edge{ 15 int to,nxt,w; 16 }e[maxn*2]; 17 int head[maxn],cnt,vis[maxn],dis[maxn]; 18 inline void add(int from,int to,int w){ 19 e[++cnt].to=to;e[cnt].w=w; 20 e[cnt].nxt=head[from];head[from]=cnt; 21 } 22 bool spfa(int x){ 23 vis[x]=1; 24 for(int i=head[x];i;i=e[i].nxt){ 25 int y=e[i].to; 26 if(dis[y]<dis[x]+e[i].w){ 27 dis[y]=dis[x]+e[i].w; 28 if(vis[y])return 0; 29 if(!spfa(y))return 0; 30 } 31 } 32 vis[x]=0; 33 return 1; 34 } 35 int n,m; 36 int main(){ 37 n=read();m=read(); 38 for(int i=1;i<=m;i++){ 39 int x,a,b,c; 40 x=read(); 41 if(x==1){ 42 a=read();b=read();c=read(); 43 add(b,a,c); 44 } 45 if(x==2){ 46 a=read();b=read();c=read(); 47 add(a,b,-c); 48 } 49 if(x==3){ 50 a=read();b=read(); 51 add(a,b,0);add(b,a,0); 52 } 53 } 54 for(int i=1;i<=n;i++){ 55 add(0,i,0); 56 dis[i]=-inf; 57 } 58 if(!spfa(0))puts("No"); 59 else puts("Yes"); 60 return 0; 61 } 62 } 63 signed main(){ 64 gengyf::main(); 65 return 0; 66 }
Luogu P3275 [SCOI 2011]糖果
题解 (再次不要脸行为)