差分拘束介紹、總結與例題
差分約束的具體概念:
如果一個系統由n個變量和m個約束條件組成,形成m個形如ai-aj≤k的不等式(i,j∈[1,n],k為常數),則稱其為差分約束系統。
例子:
假設有3個數a,b,c
我們知道:
a-b>=2
b-c>=3
a-c>=3
那麽:a與c的差值最小為多少?
a比b至少大2,b比c至少大3,那a比c就至少大5。
這很容易理解。
但是如果不等式很多呢?
100個數?1000個數?10000個數……
我們從一開始的例子開始考慮。
我們把這想象成一個圖。每個不等式就是一條有向邊。
那麽:
是不是發現跑一下最長路就可以了?
沒錯,差分拘束就是用了這個原理。
一般差分拘束問題都可以轉化為最短(長)路問題。
問題解的存在性:
1.無解:
比如a-b>=2,b-a>=1,這樣就自相矛盾了。
如果連成圖,就會發現這是一個負環。
2.無法確定:
比如只給a-b>=2,就無法判斷a與c的關系。
連成圖後表現為:a與c不連通。
不等式組的轉化:
對於題目中的不等式,一般只有轉成相同符號才方便處理。
- a-b>=t → b-a<=-t
- a-b<t → b-a<=t-1
- a-b=t → a-b<=t && a-b>=t
根據情況,按上面所說轉化。
例題:
1.LUOGU P1250 種樹
題目描述
一條街的一邊有幾座房子。因為環保原因居民想要在路邊種些樹。路邊的地區被分割成塊,並被編號成1..N。每個部分為一個單位尺寸大小並最多可種一棵樹。每個居民想在門前種些樹並指定了三個號碼B,E,T。這三個數表示該居民想在B和E之間最少種T棵樹。當然,B≤E,居民必須記住在指定區不能種多於區域地塊數的樹,所以T≤E-B+l。居民們想種樹的各自區域可以交叉。你的任務是求出能滿足所有要求的最少的樹的數量。
寫一個程序完成以下工作:
輸入輸出格式
輸入格式:
第一行包含數據N,區域的個數(0<N≤30000);
第二行包含H,房子的數目(0<H≤5000);
下面的H行描述居民們的需要:B E T,0<B≤E≤30000,T≤E-B+1。
輸出格式:
輸出文件只有一行寫有樹的數目
輸入輸出樣例
輸入樣例#1:
9
4
1 4 2
4 6 2
8 9 2
3 5 2
輸出樣例#1:
5
題解:
#include<bits/stdc++.h> #pragma GCC optimize(3) namespace ZDY{ #define res register #define ri res int #define ll long long #define db double #define sht short #define il inline #define MB template <class T> #define Fur(i,x,y) for(ri i=x;i<=y;i++) #define fur(i,x,y) for(i=x;i<=y;i++) #define Fdr(i,x,y) for(ri i=x;i>=y;i--) #define clr(x,y) memset(x,y,sizeof(x)) #define cpy(x,y) memcpy(x,y,sizeof(x)) #define fl(i,x) for(ri i=head[x],to;to=e[i].to,i;i=e[i].nxt) #define inf 2147483630 #define fin(s) freopen(s".in","r",stdin) #define fout(s) freopen(s".out","w",stdin) #define l2(n) (ceil(log2(n))) #define fast ios::sync_with_stdio(false) MB il T ABS(T x){return x>0?x:-x;} MB il T MAX(T x,T y){return x>y?x:y;} MB il T MIN(T x,T y){return x<y?x:y;} MB il T GCD(T x,T y){return y?GCD(y,x%y):x;} MB il void SWAP(T x,T y){T t=x;y=t;x=y;} }using namespace ZDY;using namespace std; #define N 30010 struct edge{int to,nxt,w;}e[N*2+5010]; int head[N],cnt=0,n,m,d[N]; bool v[N]; struct cmp{bool operator()(int a,int b){return d[a]<d[b];}}; priority_queue<int,vector<int>,cmp>q; il void add(int x,int y,int w){e[++cnt].to=y;e[cnt].w=w;e[cnt].nxt=head[x];head[x]=cnt;} il void spfa(){ int x; q.push(0); while(!q.empty()){ x=q.top();q.pop();v[x]=0; fl(i,x) if(d[x]+e[i].w>d[to]){ d[to]=d[x]+e[i].w; if(!v[to])q.push(to),v[to]=1; } } } int main(){ fast; cin>>n>>m; int x,y,w; Fur(i,1,m)cin>>x>>y>>w,add(x-1,y,w); Fur(i,0,n){ if(i!=0)add(i-1,i,0),d[i]=-inf; if(i!=n)add(i,i-1,-1); } spfa(); cout<<d[n]; }
2.LUOGU P1645 序列
題目描述
有一個整數序列,它的每個數各不相同,我們不知道它的長度是多少(即整數個數),但我們知道在某些區間中間至少有多少個整數,用區間(Li,Ri,Ci)來描述,表示這個整數序列中至少有Ci個數來自區間[Li,Ri],給出若幹個這樣的區間,問這個整數序列的長度最少能為多少?
輸入輸出格式
輸入格式:
第一行一個整數N,表示區間個數;
接下來N行,每行三個整數(Li,Ri,Ci),描述一個區間。
【數據規模】
N<=1000,0<=Li<=Ri<=1000,1<=Ci<=Ri-Li+1
輸出格式:
僅一個數,表示該整數序列的最小長度。
輸入輸出樣例
輸入樣例#1:
4
4 5 1
6 10 3
7 10 3
5 6 1
輸出樣例#1:
4
題解:
#include<bits/stdc++.h>
#pragma GCC optimize(3)
namespace ZDY{
#define res register
#define ri res int
#define ll long long
#define db double
#define sht short
#define il inline
#define MB template <class T>
#define Fur(i,x,y) for(ri i=x;i<=y;i++)
#define fur(i,x,y) for(i=x;i<=y;i++)
#define Fdr(i,x,y) for(ri i=x;i>=y;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define cpy(x,y) memcpy(x,y,sizeof(x))
#define fl(i,x) for(ri i=head[x],to;to=e[i].to,i;i=e[i].nxt)
#define inf 2147483630
#define fin(s) freopen(s".in","r",stdin)
#define fout(s) freopen(s".out","w",stdin)
#define l2(n) (ceil(log2(n)))
#define fast ios::sync_with_stdio(false)
MB il T ABS(T x){return x>0?x:-x;}
MB il T MAX(T x,T y){return x>y?x:y;}
MB il T MIN(T x,T y){return x<y?x:y;}
MB il T GCD(T x,T y){return y?GCD(y,x%y):x;}
MB il void SWAP(T x,T y){T t=x;y=t;x=y;}
}using namespace ZDY;using namespace std;
#define N 1011
struct edge{int to,nxt,w;}e[N*3];
int head[N],cnt=0,n=0,m,d[N];
bool v[N];
struct cmp{bool operator()(int a,int b){return d[a]<d[b];}};
priority_queue<int,vector<int>,cmp>q;
il void add(int x,int y,int w){e[++cnt].to=y;e[cnt].w=w;e[cnt].nxt=head[x];head[x]=cnt;}
il void spfa(){
int x;
q.push(0);
while(!q.empty()){
x=q.top();q.pop();v[x]=0;
fl(i,x)
if(d[x]+e[i].w>d[to]){
d[to]=d[x]+e[i].w;
if(!v[to])q.push(to),v[to]=1;
}
}
}
int main(){
fast;
cin>>m;
int x,y,w;
Fur(i,1,m)cin>>x>>y>>w,add(x-1,y,w),n=MAX(n,y);
Fur(i,0,n){
if(i!=0)add(i-1,i,0),d[i]=-inf;
if(i!=n)add(i,i-1,-1);
}
spfa();
cout<<d[n];
}
3.[SCOI2011]糖果
題目描述
幼兒園裏有N個小朋友,lxhgww老師現在想要給這些小朋友們分配糖果,要求每個小朋友都要分到糖果。但是小朋友們也有嫉妒心,總是會提出一些要求,比如小明不希望小紅分到的糖果比他的多,於是在分配糖果的時候,lxhgww需要滿足小朋友們的K個要求。幼兒園的糖果總是有限的,lxhgww想知道他至少需要準備多少個糖果,才能使得每個小朋友都能夠分到糖果,並且滿足小朋友們所有的要求。
輸入輸出格式
輸入格式:
輸入的第一行是兩個整數N,K。接下來K行,表示這些點需要滿足的關系,每行3個數字,X,A,B。如果X=1, 表示第A個小朋友分到的糖果必須和第B個小朋友分到的糖果一樣多;如果X=2, 表示第A個小朋友分到的糖果必須少於第B個小朋友分到的糖果;如果X=3, 表示第A個小朋友分到的糖果必須不少於第B個小朋友分到的糖果;如果X=4, 表示第A個小朋友分到的糖果必須多於第B個小朋友分到的糖果;如果X=5, 表示第A個小朋友分到的糖果必須不多於第B個小朋友分到的糖果;
輸出格式:
輸出一行,表示lxhgww老師至少需要準備的糖果數,如果不能滿足小朋友們的所有要求,就輸出-1。
輸入輸出樣例
輸入樣例#1:
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
輸出樣例#1:
11
說明
【數據範圍】
對於30%的數據,保證 N<=100
對於100%的數據,保證 N<=100000
對於所有的數據,保證 K<=100000,1<=X<=5,1<=A, B<=N
題解:
#include<bits/stdc++.h>
#pragma GCC optimize(3)
namespace ZDY{
#define res register
#define ri res int
#define ll long long
#define db double
#define sht short
#define il inline
#define MB template <class T>
#define Fur(i,x,y) for(ri i=x;i<=y;i++)
#define fur(i,x,y) for(i=x;i<=y;i++)
#define Fdr(i,x,y) for(ri i=x;i>=y;i--)
#define clr(x,y) memset(x,y,sizeof(x))
#define cpy(x,y) memcpy(x,y,sizeof(x))
#define fl(i,x) for(ri i=head[x],to;to=e[i].to,i;i=e[i].nxt)
#define inf 2147483630
#define fin(s) freopen(s".in","r",stdin)
#define fout(s) freopen(s".out","w",stdin)
#define l2(n) (ceil(log2(n)))
#define fast ios::sync_with_stdio(false)
MB il T ABS(T x){return x>0?x:-x;}
MB il T MAX(T x,T y){return x>y?x:y;}
MB il T MIN(T x,T y){return x<y?x:y;}
MB il T GCD(T x,T y){return y?GCD(y,x%y):x;}
MB il void SWAP(T x,T y){T t=x;y=t;x=y;}
}using namespace ZDY;using namespace std;
#define N 100010
struct edge{int to,nxt,w;}e[N*3];
int head[N],cnt=0,n=0,m,d[N],t[N];
bool v[N];
struct cmp{bool operator()(int a,int b){return d[a]<d[b];}};
priority_queue<int,vector<int>,cmp>q;
il void add(int x,int y,int w){e[++cnt].to=y;e[cnt].w=w;e[cnt].nxt=head[x];head[x]=cnt;}
il void spfa(){
int x;
q.push(0);
while(!q.empty()){
x=q.top();q.pop();v[x]=0;if(++t[x]>n){cout<<-1<<endl;exit(0);}
fl(i,x)
if(d[x]+e[i].w>d[to]){
d[to]=d[x]+e[i].w;
if(!v[to])q.push(to),v[to]=1;
}
}
}
int main(){
fast;
cin>>n>>m;
int p,x,y;
ll ans=0;
Fur(i,1,m){
cin>>p>>x>>y;
if(p==1)add(x,y,0),add(y,x,0);
if(p==2)add(x,y,1);
if(p==3)add(y,x,0);
if(p==4)add(y,x,1);
if(p==5)add(x,y,0);
}
Fur(i,1,n)add(0,i,1);
spfa();
Fur(i,1,n)ans+=d[i];
cout<<ans<<endl;
}
差分拘束介紹、總結與例題