洛谷p1993 差分約束+dfs版spfa

這道題算作是差分約束模板題了, 關於差分約束,我強推這位大佬的博客。https://blog.csdn.net/consciousman/article/details/53812818
裏面博客這段話就是解決這些題目的精髓了;
(下面這段話是上面博客裏面的)
/*****************************************************************************/
1、 根據條件把題意通過變量組表達出來得到不等式組,注意要發掘出隱含的不等式,比如說前後兩個變量之間隱含的不等式關係。

2、 進行建圖:

首先根據題目的要求進行不等式組的標準化。

(1)、如果要求取最小值,那麼求出最長路,那麼將不等式全部化成xi – xj >= k的形式,這樣建立j->i的邊,權值爲k的邊,如果不等式組中有xi – xj > k,因爲一般題目都是對整形變量的約束,化爲xi – xj >= k+1即可,如果xi – xj = k呢,那麼可以變爲如下兩個:xi – xj >= k, xi – xj <= k,進一步變爲xj – xi >= -k,建立兩條邊即可。

(2)、如果求取的是最大值,那麼求取最短路,將不等式全部化成xi – xj <= k的形式, 這樣建立j->i的邊,權值爲k的邊,如果像上面的兩種情況,那麼同樣地標準化就行了。

(3)、如果要判斷差分約束系統是否存在解,一般都是判斷環,選擇求最短路或者最長路求解都行,只是不等式標準化時候不同,判環地話,用spfa即可,n個點中如果同一個點入隊超過n次,那麼即存在環。

值得注意的一點是:建立的圖可能不聯通,我們只需要加入一個超級源點,比如說求取最長路時圖不聯通的話,我們只需要加入一個點S,對其他的每個點建立一條權值爲0的邊圖就聯通了,然後從S點開始進行spfa判環。最短路類似。

3、 建好圖之後直接spfa或bellman-ford求解,不能用dijstra算法,因爲一般存在負邊,注意初始化的問題。
/*************************************************************************************/

然後我們聯繫到這題目中,由於題目只是要求我們判斷能否存在,所以我們化簡不等式的時候可以選擇化大於號也可以等於號;
大於號的話就是
f[i]:表示i農場裏面的作物

  1. f[a] -f[b]>=c;
  2. f[b]-f[a]>=-c;
  3. f[a]-f[b]>=0;
  4. f[b]-f[a]>=0;

也可以把上面式子以爲變成全部小於的。
對於上面式子,用博客裏面的做法,就是

  1. b連a,權值是c
  2. a連b,權值是-c
  3. a連b,權值是0
  4. b連a,權值是0

然後跑最長路。小於號的就邊的權和方向改成小於號的形式在跑最短路。
然後由於只是需要判斷是否成環,就用dfs版的spfa最快。
下面是代碼

#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
using namespace std;
const int max_ = 1000000 + 7;
int head[max_], xiann = 1, n, m;
inline int read()
{
	int s = 0, f = 1;
	char ch = getchar();
	while (ch<'0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0'&&ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}
struct k {
	int next, to, value;
}xian[max_ * 10];
void add_(int a, int b, int c) {
	xian[xiann].next = head[a];
	xian[xiann].to = b;
	xian[xiann].value = c;
	head[a] = xiann;
	xiann++;
}

int f[max_],vis[max_];
int spfa(int now) {
	vis[now] = 1;
	for (int i = head[now]; i; i = xian[i].next) {
		if (f[now] + xian[i].value > f[xian[i].to]) {
			f[xian[i].to] = f[now] + xian[i].value;
			if (vis[xian[i].to]) return 0;//如果一個點通過一段路徑後回到了自己且這段路徑還比之前的優,意即存在所尋找的環,返回
			if (!spfa(xian[i].to)) return 0;//有過return 0的情況;
		}
	}
	vis[now] = 0;
	return 1;
}
int main() {
	n = read(); m = read();
	while (m--) {
		int t = read(), a = read(), b = read(), c;
		switch (t) {
		case 1: {
			c = read();
			add_(b, a, c);
			break;
		}
		case 2: {
			c = read();
			add_(a, b, -c);
			break;
		}
		case 3: {
			add_(a, b, 0);
			add_(b, a, 0);
			break;
		}
		default:
			break;
		}
	}
	for (int i = 1; i <= n; i++) {
		add_(0, i, 0);
		f[i] = -max_;
	}
	if (!spfa(0)) cout << "No";
	else{
		cout << "Yes";
	   }
    
return 0;
}
發佈了37 篇原創文章 · 獲贊 4 · 訪問量 4728
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章