zoukankan      html  css  js  c++  java
  • [笔记]差分约束系统

    [笔记]差分约束系统

    算法用途

    ​ 当题面给出许多形如(x_i - x_j ≤ c_k(c为常数))的不等式时,并让你求出一组满足条件的解时,就可以运用差分约束系统.

    算法描述

    ​ 首先,我们对不等式进行变形,变成(x_i ≤ x_j + c_k)的形式,这时我们可以发现,这和求最短路时的式子((dis[v] ≥ dis[u] + w_{u→v})))很像,但我们发现这两个式子的不等号方向相反,但这其实并不影响我们用最短路的方法来解决问题。

    ​ 首先我们假设有一个不等式组

    [egin{cases} t_i ≤ t_{j'} + b\ t_i ≤ t_{j''} + b\ t_i ≤ t_{j'''} + b end{cases} ]

    那么要使(t_i)满足所有不等式并且取到最大值,则答案应该是(min(t_{j'},t_{j''},t_{j'''}) + b),因为如果要满足所有不等式则要取最小值(同小取小),实际情况(t_j)集合的元素可能不止(3)个,我们直接用(t_j)来表示所有(t_j)集合中的元素,则答案可以表示为(t_i = min(t_j + b)),这和(SPFA)中的式子((dis_{ti} = min(dis_{tj} + w_{i→j}))一样,因此我们证明了差分约束的问题可以用最短路来解决。具体方法是连一条从(j)(i)的权值为(b)的有向边,再跑(SPFA)即可

    ​ 通过上面的推理我们知道当我们跑最短路时,可以求出满足条件的最大解,那么我们可以同理推出当我们用(SPFA)跑最长路时可以求出满足条件的最小值,那么究竟什么时候跑最短路,什么时候跑最长路呢?

    例题讲解

    ​ 下面我们来看两道题目。T1,T2.

    ​ 在第一题中,题目的要求与上文我们举的例子相符合,因此是跑最短路。

    ​ 再来看第二题,题目要求的是让我们在所有区间(a_i)(b_i)中均选出不少于(c_i)个数来满足要求。我们假设(t_i)表示的是在前(i)个数中选了(t_i)个,那么我们可以得到如下不等式组:

    [egin{cases} t_j - t_i ≥ c_{i,j}\ t_{j'} - t_{i'} ≥ c_{i',j'}\ t_{j''} - t_{i''} ≥ c_{i'',j''}\ end{cases} ]

    类比上文的建图方法,我们以同样的方式建边,但要注意的是区间(i→j)所有选的数的个数应该表示为(t_j - t_{i - 1})因为第(i)位的数字也可能被选中。但是这一个条件并不全, 我们知道一定会有(0≤t_i - t_{i+1} ≤ 1),因为可能存在第(i+1)位选或不选的情况,但由于一个建边的条件并不能满足两个不等式(上式可以拆分为两个不等式),所以我们通过两个条件来约束.根据拆分完的不等式来进行约束(t_{i+1}-t_i ≥ 0)(t_{i+1} ≥ t_i + 0)因此从(i)连一条到(j)的权值为(0)的有向边;第二个不等式是(t_i - t_{i+1} ≤ 1)(t_i ≤ t_{i+1} + 1),因此从(i+1)连一条到(i)的权值为(0)的有向边,这样就没有漏过任何一个约束条件了。

    代码

    T1

    #include <bits/stdc++.h>
    using namespace std;
    struct node{int to,next,w;}e[50010];
    int fir[50010],tot = 0,times[50010],n,m;
    void add(int x,int y,int z){
    	tot++;
    	e[tot].w = z;
    	e[tot].to = y;
    	e[tot].next = fir[x];
    	fir[x] = tot;
    	return;
    }
    bool in[50010];
    int dis[50010];
    bool spfa(int x){
    	queue < int > q;
    	for(int i = 1;i <= n;i++)dis[i] = 1e9;
    	while(!q.empty())
    		q.pop();
    	memset(in,false,sizeof(in));
    	memset(times,0,sizeof(times));
    	in[x] = true;
    	times[x]++;
    	dis[x] = 0;
    	q.push(x);
    	while(!q.empty()){
    		int k = q.front();
    		q.pop();in[k] = false;
    		for(int i = fir[k];i;i = e[i].next){
    			if(dis[e[i].to] > dis[k] + e[i].w){
    				dis[e[i].to] = dis[k] + e[i].w;
    				if(!in[e[i].to]){
    					q.push(e[i].to);
    					times[e[i].to]++;
    					in[e[i].to] = true;
    					if(times[e[i].to] > n)
    						return false;
    				}
    			}
    		}
    	}
    	return true;
    }
    int main(){
    	cin>>n>>m;
    	for(int i = 1;i <= m;i++){
    		int x,y,z;
    		cin>>x>>y>>z;
    		add(y,x,z);
    	}
    	for(int i = 1;i <= n;i++){
    		add(0,i,0);
    	}
    	if(!spfa(0)){
    		cout<<"NO"<<endl;
    		return 0;
    	}
    	for(int i = 1;i <= n;i++){
    		cout<<dis[i]<<" ";
    	}
    	cout<<endl;
    	return 0;
    }
    

    T2

    #include <bits/stdc++.h> 
    using namespace std;
    struct node{
    	int to,next,w;
    }edge[150010];
    const int inf = INT_MAX;
    long long fir[150010],tim[150010],dis[150010],tot,n,maxx,minn;
    bool in[150010];
    void add(int x,int y,int z){
    	tot++;
    	edge[tot].to = y;
    	edge[tot].next = fir[x];
    	edge[tot].w = z;
    	fir[x] = tot;
    }
    void spfa(){
    	queue < int > q;
    	while(!q.empty())q.pop();
    	memset(in,false,sizeof(in)); 
    	memset(tim,0,sizeof(tim));
    	memset(dis,-63,sizeof(dis));
    	dis[minn] = 0;in[minn] = true;tim[minn] = 1;q.push(minn);
    	while(!q.empty()){
    		int x = q.front();q.pop();
    		in[x] = false;
    		for(int i = fir[x];i != -1;i = edge[i].next){
    			if(dis[edge[i].to] < dis[x] + edge[i].w){
    				dis[edge[i].to] = dis[x] + edge[i].w;
    				if(!in[edge[i].to]){
    					tim[edge[i].to]++;
    					if(tim[edge[i].to] > maxx)return;
    					in[edge[i].to] = true;
    					q.push(edge[i].to);
    				}
    			}
    		}
    	}
    	return;
    }
    int main(){
    		scanf("%d",&n);
    		memset(fir,-1,sizeof(fir));
    		memset(edge,0,sizeof(edge));
    		maxx = -1;minn = inf;
    		tot = 0;
    		for(int i = 1;i <= n;i++){
    			long long x,y,z;
    			scanf("%lld%lld%lld",&x,&y,&z);
    			if(x > y)swap(x,y);
    			add(x - 1,y,z);
    			maxx = max(maxx,y);
    			minn = min(minn,x - 1);
    		}
    		for(int i = minn;i <= maxx;i++){
    			add(i,i + 1,0);
    			add(i + 1,i,-1);
    		}
    		spfa();
    		printf("%d
    ",dis[maxx]);
    	return 0;
    }
    

    完结撒花

  • 相关阅读:
    &与&&的区别
    x^y=(x&~y)|(~x&y)证明
    a、b交换与比较
    x+y = ((x&y)<<1) + (x^y) 证明
    (x&y) + ((x^y)>>1)即x和y的算数平均值
    默认参数提升
    类型转换
    闲扯原码,补码和反码(转)
    C/C++中float和double的存储结构
    led设备驱动(s3c_led.c)
  • 原文地址:https://www.cnblogs.com/czy--blog/p/13663686.html
Copyright © 2011-2022 走看看