洛谷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
#include
#include
#include
#include
#include
#include
#include
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;
}

你可能感兴趣的:(学习,算法,算法,差分约束,图,spfa,dfs)