• 差分约束


    差分约束

    讲解

    如果一个不等式组由 (n) 个变量和 (m) 个约束条件组成,形成 (m) 个形如(x[ j ]-x[ i ]≤k)(i,j∈[1,n])(k) 为常数)的不等式,则称其为差分约束系统。换句话说,差分约束系统就是求解一组变量的不等式组的算法。

    (d_v-d_u<=w) 和 spfa 中的松弛操作 (d_v<=d_u+w) 非常像
    只要没有负环则能得到⼀组合法的{(d_i)} ,则原问题有解且 {(d_i)} 就是⼀组可行解

    做题步骤

    1.判断是让求至多(最短路)还是至少(最长路)

    2.按照最短/长路松弛操作,对应题目约束条件连边

    3.看题目中一些特殊要求,继续完善连边

    4.建立超级源点0,向每个点连边权为 0 的边

    最后的答案输出:

    一般是让输出 dis[n] , dis[1~n], 无解:最短路有负环,最长路有正环(用cnt[ ]判断每个点取出次数是不是≥n )

    spfa

    k;相当于限制(x[ i ])的最大/小值

    连边后求最短路
    (x[ i ]−x[ j ]≤k) 变形为 (x[ i ]≤k+x[ j ]),即从 (j)(i) 连一条边权为 (k) 的边。加入超级源点后求最短路,得到(x[i] ≤ k) 所有 (x) 最大解。

    连边后求最长路
    (x[ i ]−x[ j ]≥k) 变形为 (x[ j ]≤x[ i ]-k),即从 (i)(j) 连一条边权为 (-k) 的边。加入超级源点后求最长路,得到(x[i]≥k) 所有 (x) 最小解。

    判负环spfa必然退化为O(nm)

    其他未写的题目:

    附上一篇有超详细解析和题目的blog

    模板

    一定注意建超级源点

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int N=5005;
    inline 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*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m;
    int hd[N],to[N<<1],tot,nxt[N<<1],w[N<<1];
    inline void add(int x,int y,int z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;	
    }
    queue<int>q;
    int dis[N],cnt[N];
    bool vis[N];
    bool spfa() {
    	q.push(0);
    	dis[0]=0;
    	cnt[0]=1;
    	while(q.size()) {
    		int x=q.front();q.pop();
    		vis[x]=0;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y]>dis[x]+w[i]) {
    				dis[y]=dis[x]+w[i];
    				if(++cnt[y]>n) return 1;
    				if(!vis[y]) vis[y]=1,q.push(y);
    			}
    		}
    	}
    	return 0;
    }
    int main() {
    	memset(dis,0x3f,sizeof(dis));
    	n=read();m=read();
    	for(int i=1;i<=m;i++) {
    		int u=read(),v=read(),w=read();
    		add(v,u,w);
    	}
    	for(int i=1;i<=n;i++)
    		add(0,i,0);
    	if(spfa()) puts("NO");
    	else {
    		for(int i=1;i<=n;i++)
    			printf("%d ",dis[i]);
    	}
    	return 0;
    }
    

    种树

    求最少种树量——最长路

    (dis[y]<dis[x-1]+w[i]) —— 区间 ([x,y])至少种 (w[i]) 棵树

    for(int i=0;i<=n;i++){
    	if(i!=0)add(i-1,i,0),dis[i]=-inf; // [i-1,i]最少种0棵
    	if(i!=n)add(i,i-1,-1);//[i-1,i]最多种1棵
    }
    
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int N=50005;
    inline 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*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    const int inf=0x3f3f3f3f;
    int n,m;
    int hd[N],to[N<<1],tot,nxt[N<<1],w[N<<1];
    inline void add(int x,int y,int z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;	
    }
    queue<int>q;
    int dis[N],cnt[N];
    bool vis[N];
    bool spfa() {
    	q.push(0);
    	dis[0]=0;
    	cnt[0]=1;
    	while(q.size()) {
    		int x=q.front();q.pop();
    		vis[x]=0;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y]<dis[x]+w[i]) {
    				dis[y]=dis[x]+w[i];
    				if(++cnt[y]>n) return 1;
    				if(!vis[y]) vis[y]=1,q.push(y);
    			}
    		}
    	}
    	return 0;
    }
    int main() {
    	n=read();m=read();
    	for(int i=1;i<=m;i++) {
    		int u=read(),v=read(),w=read();
    		add(u-1,v,w);
    	}
    	for(int i=0;i<=n;i++) {
    		if(i) add(i-1,i,0),dis[i]=-inf;
    		if(i!=n) add(i,i-1,-1);
    	}
    	if(spfa()) puts("NO");
    	else printf("%d ",dis[n]);
    	return 0;
    }
    

    也可用贪心和线段树优化

    POJ 3169 Layout

    https://www.luogu.com.cn/problem/P4878

    注意限制 i+1 ---> i 连 0 (牛可以在同一个位置)

    从0开始跑spfa判断图是不是联通的——洛谷毒瘤hack

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=2005;
    const int M=1000005;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m1,m2;
    int hd[N],to[M],tot,nxt[M],w[M],cnt[N];
    inline void add(int x,int y,int z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
    }
    bool vis[N];
    long long dis[N];
    bool spfa(int x) {
    	queue<int>q;
    	for(int i=0;i<=n;i++) dis[i]=0x3f3f3f3f,vis[i]=0;
    	q.push(x);dis[x]=0;
    	while(!q.empty()) {
    		int x=q.front();q.pop();
    		vis[x]=0;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y]>dis[x]+w[i]) {
    				dis[y]=dis[x]+w[i];
    				if(!vis[y]) {
    					vis[y]=1;q.push(y);
    					if(++cnt[y]==n) {
    						puts("-1");return 1;
    					}
    				}				
    			}
    		}
    	}
    	return 0;
    }
    int main() {
    	n=read();m1=read();m2=read();
    	int a,b,c;
    	for(int i=1;i<=m1;i++) {
    		a=read();b=read();c=read();
    		add(a,b,c);
    	}
    	for(int i=1;i<=m2;i++) {
    		a=read();b=read();c=read();
    		add(b,a,-c);
    	}
    	for(int i=1;i<=n;i++)
    		add(0,i,0);
    	for(int i=1;i<n;i++)
    		add(i+1,i,0);
    	if(spfa(0)) return 0;
    	spfa(1);
    	printf("%lld
    ",dis[n]==0x3f3f3f3f?-2:dis[n]);
    	return 0;
    }
    
    

    小 K 的农场

    • 农场a比农场b至少多种植了c个单位的作物, (dis[a]-dis[b]>c) , (dis[b]<dis[a]-c) ; (a)(b)连一条 (-c)
    • 农场a比农场b至多多种植了c个单位的作物,(dis[a]-dis[b]<c) , (dis[a]<dis[b]+c) ; (b)(a)连一条 (c)
    • 农场(a)与农场(b)种植的作物数一样多。(a-->b)连一条(0) (b-->a)连一条(0)

    然后快乐AC

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const int N=100005;
    inline 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*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    const int inf=0x3f3f3f3f;
    int n,m;
    int hd[N],to[N<<1],tot,nxt[N<<1],w[N<<1];
    inline void add(int x,int y,int z) {
    	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;	
    }
    queue<int>q;
    int dis[N],cnt[N];
    bool vis[N];
    bool spfa() {
    	memset(dis,0x3f,sizeof(dis));
    	q.push(0);
    	dis[0]=0;
    	cnt[0]=1;
    	while(q.size()) {
    		int x=q.front();q.pop();
    		vis[x]=0;
    		for(int i=hd[x];i;i=nxt[i]) {
    			int y=to[i];
    			if(dis[y]>dis[x]+w[i]) {
    				dis[y]=dis[x]+w[i];
    				if(++cnt[y]>n) return 1;
    				if(!vis[y]) vis[y]=1,q.push(y);
    			}
    		}
    	}
    	return 0;
    }
    int main() {
    	n=read();m=read();
    	for(int i=1;i<=m;i++) {
    		int op=read(),u=read(),v=read(),ww;
    		if(op==1) ww=read(),add(u,v,-ww);
    		else if(op==2) ww=read(),add(v,u,ww);
    		else add(u,v,0),add(v,u,0);
    	}
    	for(int i=1;i<=n;i++) add(0,i,0);
    	puts(spfa()?"No":"Yes");
    	return 0;
    }
    

    糖果

    这题卡邻接表???vector 可过

    好像正解是tarjan —— this

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    #include<utility>
    using namespace std;
    #define N 300005
    #define inf 0x3f3f3f3f
    inline int read(){
    	int x=0;char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x;
    }
    vector < pair<int,int> > g[N];
    queue < int > q;
    int n,m;
    int dis[N],cnt[N];
    bool vis[N],f;
    long long ans;
    int main(){
    	n=read();m=read();
    	for(int i=1,op,x,y;i<=m;i++){
    		op=read();x=read();y=read();
    		if(op==1) g[x].push_back(make_pair(y,0)),g[y].push_back(make_pair(x,0));
    		else if(op==2) g[x].push_back(make_pair(y,1));
    		else if(op==3) g[y].push_back(make_pair(x,0));
    		else if(op==4) g[y].push_back(make_pair(x,1));
    		else g[x].push_back(make_pair(y,0));
    		if(x==y&&(op==2||op==4)){puts("-1");return 0;}
    	}
    	for(int i=1;i<=n;i++)
    		g[0].push_back(make_pair(i,1));
    	memset(dis,-0x3f,sizeof(dis));
    	q.push(0);vis[0]=1;dis[0]=0;
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		vis[x]=0;
    		for(int i=0,siz=g[x].size();i<siz;i++){
    			int y=g[x][i].first,w=g[x][i].second;
    			if(dis[y]<dis[x]+w){
    				dis[y]=dis[x]+w;
    				if(++cnt[y]==n){ puts("-1"); return 0;}
    				if(!vis[y]){
    					vis[y]=1;q.push(y);
    				}
    			}
    		}
    	}
    	for(int i=1;i<=n;i++)
    		ans+=dis[i];
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Git分支管理策略
    嵌入式文件系统构建工具 busybox / buildroot / openwrt
    nodejs与c语言交互应用实例
    python与c语言交互应用实例
    websocket programming base on nodejs
    Using Bluetooth LE with Go
    nodejs
    linux ipc/its
    SAMA5D3 Xplained Board
    BlueZ
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13596940.html
Copyright © 2020-2023  润新知