差分约束+判正环
差分约束:
给定几个形如(a_i-b_i<=c_i)或(a_i-b_i>=c_i)的不等式。
一般问题都会问(a_i-b_j)的最小值或最大值,或者是否所有条件都满足。
松弛操作(dis[now]-dis[to]<e[i].len)与上文的不等式十分相似,然后考虑最小值,最大值和是否满足等问题转化为图论问题。为了方便,先选择将不等式都转为一个符号(习惯因人而异)
小于号: 都转移到小于号后,假设如下不等式b-a<=c这句话如果转化到图中,意思是一个点x到另一个点y当前的边是c,x到y的最短路不可能比c还大,就是还可能会有更短的最短路连接x,y,此时需要向图中加边(x->y=c);加入原题要问b-a的最大值,则转化到图中就是x到y的最小值因为最大值要所有路径都要满足。如果有条件不满足则说明会存在两点间的最小值会不断更新,即图中存在负环,或者会存在两点间根本不相连,此时他们之间的最大值可以取无数解
大于号类似。
#include <bits/stdc++.h>
#define N 10001011
using namespace std;
struct edg {
int to, nex, len;
}e[N];
int lin[N], dis[N], cnt, n, m;
inline void add(int f, int t, int l)//添加一组松弛关系
{
e[++cnt].len = l;
e[cnt].to = t;
e[cnt].nex = lin[f];
lin[f] = cnt;
}
int vis[N], b[N], flag;
void dfs(int now)
{
b[now] = 1;
for (int i = lin[now]; i; i = e[i].nex)
{
int to = e[i].to;
if (dis[now] + e[i].len > dis[to])//当前节点还没有被找到过,或者已经被找到过但是又重新到了这个点上。
{
if (b[to])
printf("No"), exit(0);
dis[to] = dis[now] + e[i].len;
dfs(to);
}
}
b[now] = 0;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int flag, a, b, c;
scanf("%d", &flag);
if (flag == 1)//a - b > c
{
scanf("%d%d%d", &a, &b, &c);
add(b, a, c);//a比b至少大c
}
if (flag == 2)//a-b<c
{
scanf("%d%d%d", &a, &b, &c);
add(a, b, -c);//b比a至少小c,意思就是a比b至多大c
}
if (flag == 3)
{
scanf("%d%d", &a, &b);
add(b, a, 0), add(a, b, 0);//a比b至少大0,b比a至少大0
}
}
for (int i = 1; i <= n; i++)
add(0, i, 0), dis[i] = -2147483647;
dfs(0);
printf("Yes");
return 0;
}