差分約束
阿新 • • 發佈:2020-09-23
前言
沒啥好說的,存個板子,感覺這東西這輩子都不會考到
講解
保留環節:百度百科自學時間
差分約束系統是用於求包含n個未知數,m個不等式,形如:
\[\begin{cases} x_{c_1}-x_{c_1'}\le y_1\\ x_{c_2}-x_{c_2'}\le y_2\\ ...\\ x_{c_m}-x_{c_m'}\le y_m \end{cases}\]的方程組的解的一種演算法
試圖大喘氣
我們提出一個式子來觀察:
\(x_{i}-x_{j}\le y\)
將其轉換一下:
\(x_i\le y+x_j\)
誒!這是不是跟最短路的更新有點像,這可以轉換成什麼?
\(j\)到\(i\)
這相當於什麼?直接對於\(j\)向\(i\)連一條權值為\(y\)的邊
然後跑最短路即可
什麼,有可能出現負環?
我們思考一下負環意味著什麼
\(x_i\le y+x_i(y<0)\)
這不是顯然無解?
當然,我們根據這種方法構造出來的圖有可能不是連通的,只需要構建一個超級源點\(0\),對其向\(1\)到\(n\)每個點連一條權值為\(0\)的邊即可
當然由於存在負邊權,我們不能使用Dijkstra,只能使用SPFA
因為我不會Bellman-Ford
拓展
\(x_i-x_j\ge y\)的形式可以兩邊同乘\(-1\)改變符號
\(x_i-x_j=y\)
練習
程式碼
板題程式碼
int head[MAXN],tot; struct edge { int v,w,nxt; }e[MAXN << 1]; void Add_Edge(int x,int y,int z) { e[++tot].v = y; e[tot].w = z; e[tot].nxt = head[x]; head[x] = tot; } int dis[MAXN],cnt[MAXN]; bool vis[MAXN]; void spfa() { queue<int> q; q.push(0); vis[0] = 1; for(int i = 1;i <= n;++ i) dis[i] = INF; while(!q.empty())//正常SPFA { int t = q.front(); q.pop(); cnt[t]++; vis[t] = 0; if(cnt[t] > n) {printf("NO");return;}//存在負環 for(int i = head[t]; i ;i = e[i].nxt) if(dis[e[i].v] > dis[t] + e[i].w) { dis[e[i].v] = dis[t] + e[i].w; if(!vis[e[i].v]) q.push(e[i].v),vis[e[i].v] = 1; } } for(int i = 1;i <= n;++ i) Put(dis[i],' '); } int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); n = Read(); m = Read(); for(int i = 1;i <= m;++ i) { int u = Read(),v = Read(); Add_Edge(v,u,Read());//注意連邊順序與權值 } for(int i = 1;i <= n;++ i) Add_Edge(0,i,0);//建立超級源點與邊 spfa(); return 0; }