1. 程式人生 > 實用技巧 >[筆記]差分約束演算法

[筆記]差分約束演算法

[筆記]差分約束演算法

原題鏈

演算法

​ 題目給出了幾個刑辱x1 - x2≤y1的約束條件,我們可以將其移項,變形為x1≤y1+x2,這就類似於最短路中的式子dis[y] ≤dis[x] + w[z],所以我們可以仿照最短路演算法.連一條從x2到x1的權值為y1的有向邊,這是我們新建一個虛擬節點0,將所有點i都連一條從0指向i的邊權為0的有向邊,因此問題的答案便成為了從0到任意節點i的最短路徑的長度,又因為與0相連的邊的權值為0,所以對答案沒有影響.

AC程式碼

#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;
}

結束