1. 程式人生 > >POJ 3159 差分約束系統 最短路

POJ 3159 差分約束系統 最短路

題意:分糖果,有n個人,m個要求,每個要求的描述是A,B,C,代表B的糖果數-A的糖果數<= C。問n的糖果數 - 1的糖果數最大值為多少。 很明顯可以看出來是差分約束系統,但是自己對差分約束系統的理解還不是很透徹,對於跑最長路還是最短路以及得出的解的性質以及超級源點的設定的理解還不是很徹底。等有時間一定再去學一學。 //接下來這一段思維混亂,解題思路簡單解釋就是按d[B]<=d[A]+C建圖A到B有權值為C的邊,跑最短路。
這道題最後問d[n]-d[1]的最大值,就是說是1到n的最長路,約束條件為d[B]-d[A] <=C,是最短路的鬆弛形式。不過我們變下形就是d[A] >= d[B]+(-C)。從B到A有一條權值為-C的邊,在這樣一個圖上跑最長路。其實就等於在d[B]<=d[A]+C這樣一個正權的圖上跑最短路,從A到B建立一條權值為C的邊,沒有建超級源。但是前者跑出的結果並不對,而且如果把邊建成A到B的一條權值為-C的邊再跑最長路就正確了(這不就與跑最短路本質相同了麼?),具體是為什麼,自己還沒理解。
但是寫個SPFA還是會的,注意用vector存圖以及再用STL的佇列跑SPFA會T,最後就改成了鄰接表以及陣列模擬棧(模擬佇列首尾邊界判斷麻煩)才AC。
#include <cstdio>
#include <cstring>
#define M 150005
#define K 30005

int n, m, d[K], q[K];
int h[K], nx[M], v[M], w[M];
bool inq[K];

int main()
{
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= m; i++){
		int u;
		scanf("%d %d %d", &u, v+i, w+i);
		nx[i] = h[u];
		h[u] = i;
	}	
	
	memset(d, 0x7f, sizeof d);
	d[1] = 0;
	int top = 0;
	q[++top] = 1;
	while(top){
		int u = q[top--];
		inq[u] = 0;
		for(int i = h[u]; i; i = nx[i])
			if(d[v[i]] > d[u]+w[i]){
				d[v[i]] = d[u]+w[i];
				if(!inq[v[i]]){
					inq[v[i]] = 1;
					q[++top] = v[i];
				}
			}
	}
	printf("%d", d[n]);
} 
#include <cstdio>
#include <cstring>
#define M 150005
#define K 30005

int n, m, d[K], q[K];
int h[K], nx[M], v[M], w[M];
bool inq[K];

int main()
{
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= m; i++){
		int u;
		scanf("%d %d %d", &u, v+i, w+i);
		nx[i] = h[u];
		h[u] = i;
	}	
	
	memset(d, -0x7f, sizeof d);
	d[1] = 0;
	int top = 0;
	q[++top] = 1;
	while(top){
		int u = q[top--];
		inq[u] = 0;
		for(int i = h[u]; i; i = nx[i])
			if(d[v[i]] < d[u]-w[i]){
				d[v[i]] = d[u]-w[i];
				if(!inq[v[i]]){
					inq[v[i]] = 1;
					q[++top] = v[i];
				}
			}
	}
	printf("%d", -d[n]);
}