一、算法簡介
差分約束系統是一種特殊的N元一次不等式組,它包含N個變量,X1~Xn以及m個約束條件,每一個約束條件都是由兩個變量作差構成的,形如Xi-Xj≤Ck,其中Ck是常數,1≤i,j≤N,1≤k≤M,解決的問題是求一組解,X1=a1,X2=a2~~~Xn=an,使得所有的約束條件都得到滿足。
差分約束系統的每一個約束條件Xi-Xj≤Ck可以變形為最短路中的三角不等式,Xi≤Xj+Ck,因此,可以把每一個變量Xi看作有向圖中的一個結點i,對於每一個約束條件Xi-Xj≤Ck,為從結點j想結點i連一條長度為Ck的有向邊
二、例題
此題是差分約束的裸題,但我之前也不知道差分約束系統是什么。
定義:差分約束系統是一種特殊的N元一次不等式,包含N個變量及M個約束條件,每個約束條件由兩個變量作差構成,形如Xi-Xj>=Ck,Ck是常數,1<=i,j<=N,1<=k<=M。如
X1-X0>=2 X2-X1>=3 X3-X2>=2 X3-X0>=6
通過消元可得到X3-X0的最小值為7。假若我們從0->1建一條權值為2的邊,從1->2建一條權值為3的邊,以此類推。從0到3跑一遍SPFA求最長路,發現也為7。
這不是個巧合,因為求最長路時,SPFA的松弛操作后,dis[v]>=dis[u]+g[i].w,與不等式Xi>=Xj+Ck性質一樣,所以SPFA可以求出最小值,又因為不等式同大取大,所以要求出最長路。所以SPFA死了嗎?
最后,一定要注意輸出,每個答案空一行,最后一個答案最后要輸出換行。
本題就是從0-50000選出最少的數,使每個區間至少有c個數被選,這是求最小值,所以我們待會要將所有的不等式轉換成">="的形式 我們用數組d[i]表示0-i之間至少要選多少個數,所以對於每一個約束條件,可以看成d[b_i]-d[a_i-1]>=c_id[bi]−d[ai−1]>=ci
d[bi]>=ci+d[ai−1]
因此我們在a[i-1]和b[i]之間建一條長度為c[i]的邊
上述建邊過程只是題干中給出的條件,我們還要找出題目中的隱藏條件(隱藏條件視題目而為,有則加)。
因為d[i]表示的是0-i之間要選的最小個數,所以d[i]一定要大於d[i-1];又因為每個數只能選一次,所以d[i]-d[i-1]要么為0,要么為1;
由上述兩個條件,我們對應出兩個不等式d_i-d_{i-1}>=0di−di−1>=0d_{i-1}-d_{i}>=-1di−1−di>=−1
所以我們在i-1和i之間連長度為0的有向邊,在i和i-1之間連長度為-1的有向邊
因為i的范圍是0-50000所以i-1會變成負數,我們都知道,若數組的下標為負,程序會運行錯誤,所以我們不妨給所有的變量加1,這樣就不會出現負下標了,所以上述加邊過程可簡述為
1---->在a[i]和b[i]+1之間連一條長度為c[i]的有向邊 2---->在i和i+1之間連一條長度為0的有向邊 3---->在i+1和i之間連一條長度為-1的有向邊
接着,設d[0]為1,以0為起點求單元最長路
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; int t,n,head[500005],tot,ma,dis[500005]; bool vis[500005]; struct node{ int u,v,next,w; }g[5000005]; inline int rea(){//快讀 int s=0,g=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') g=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return s*g; } inline void add(int u,int v,int w){//建圖 g[++tot].u=u; g[tot].v=v; g[tot].w=w; g[tot].next=head[u]; head[u]=tot; } inline void spfa(){//SPFA求最長路,即可得出最小值 queue<int>q; q.push(0); vis[0]=true; dis[0]=0; while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=false; for(int i=head[u];i;i=g[i].next){ int v=g[i].v; if(dis[v]<dis[u]+g[i].w){ dis[v]=dis[u]+g[i].w; if(!vis[v]){ vis[v]=true; q.push(v); } } } } } int main(){ t=rea(); while(t--){ memset(head,0,sizeof(head)); memset(dis,-0x3f,sizeof(dis)); tot=0; n=rea(); for(int i=1;i<=n;i=-~i){ int a=rea(),b=rea(),c=rea(); add(a,b+1,c);//因為前綴和中,Sb-S(a-1)>=c,然而a-1要大於等於0,a有可能是0,所以我們加一 ma=max(ma,b+1); } for(int i=1;i<=ma;i=-~i){ add(i-1,i,0); add(i,i-1,-1);//Si-S(i-1)>=0,S(i-1)-Si>=-1 } spfa(); printf("%d\n",dis[ma]); if(t) printf("\n");//注意輸出!!! } return 0; }
三、相關文章鏈接
https://blog.csdn.net/dragon60066/article/details/80245797