这道题算作是差分约束模板题了, 关于差分约束,我强推这位大佬的博客。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;
}