這道題算作是差分約束模板題了, 關於差分約束,我強推這位大佬的博客。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農場裏面的作物
- f[a] -f[b]>=c;
- f[b]-f[a]>=-c;
- f[a]-f[b]>=0;
- f[b]-f[a]>=0;
也可以把上面式子以爲變成全部小於的。
對於上面式子,用博客裏面的做法,就是
- b連a,權值是c
- a連b,權值是-c
- a連b,權值是0
- 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;
}