题目描述
小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共m个),以下列三种形式描述:
农场a比农场b至少多种植了c个单位的作物, 农场a比农场b至多多种植了c个单位的作物, 农场a与农场b种植的作物数一样多。
但是,由于小K的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。输入格式 第一行包括两个整数 n 和 m,分别表示农场数目和小 K 记忆中的信息数目。
接下来 m 行:
如果每行的第一个数是 1,接下来有 3 个整数 a,b,c,表示农场 a 比农场 b 至少多种植了 c 个单位的作物。
如果每行的第一个数是 2,接下来有 3 个整数 a,b,c,表示农场 a 比农场 b 至多多种植了 c 个单位的作物。如果每行的第一个数是
3,接下来有 2 个整数 a,b,表示农场 a 种植的的数量和 b 一样多。输出格式 如果存在某种情况与小 K 的记忆吻合,输出“Yes”,否则输出“No”。
输入输出样例
输入 #1 复制
3 3
3 1 2
1 1 3 1
2 2 3 2
输出 #1 复制
Yes
说明/提示 对于 100% 的数据保证:1 ≤ n,m,a,b,c ≤ 10000。
思路
- 这题就是差分约束,但是是我这蒟蒻懵了半天,
一直蒙在 “为什么有时候要跑最短路、有时候要跑最长路,都跑 最短路径不行吗?”,后来才逐渐明白了点,跑最短路的时候是为了找 (题目所需要)最大的答案;跑最长路径是为了找(题目所需要的)的小答案
。。。。。。但是,我们总是觉的很怪异,为啥 跑最长路的出来的结果是 最小答案呢???,, - 我其实我们可以考虑 在跑最长的的时候, 我们要把所有的差分约束条件变成 “ >= 的形式,
我们可以这样想 假如 a >= 10 ,那么 a的最小值就是 10,当把我们 所有的约束条件变成 >= 形式的时候,当我们Spfa跑完最长路经的时候,在图中任意的节点 u、v,对应边权为w ,一定满足dis[ v ] >= dis[ u] + w = 某个值
. … …所以同理得出的是最小答案 - 这样想了之后,我们可以 推测出为什么 跑 最短路 出来的结果 为什么是最大答案了。。
还有最后一点做题时要先弄明白:
- 题目让我们求最大 ans 的时候 ,我们要把所有的等式变成 <= 的形式,并且跑最短路就行了,考虑这个时候点最短路建边 ,,,例如 :a - b <= 10 =》. a <= b+10, 我们可以 建一条权值为 10,b -> a 的边 ,
- 题目让我们求最大 ans 的时候 ,我们要把所有的等式变成 >= 的形式,并且跑最长路就行了,考虑这个时候点最短路建边 ,,,例如 :a - b >= 10 =》. a >= b+10, 我们可以 建一条权值为 10,b -> a 的边 …照着公式建边就行了。。
题解一(80分不开02优化)
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x7ffffff
const int maxn = 100005;
const int maxm = 200005;
struct Edge
{
int v,w,next;
} edge[maxm];
int head[maxm];
int dis[maxm];
int use[maxm];
int cnt[maxm];
int k = 0;
int n,m;
void Add(int u, int v, int w)
{
edge[++ k] = (Edge){ v, w, head[u]}; head[u] = k;
}
bool Spfa(int s)
{
for(int i = 0; i <= n; i ++)
dis[i] = INF, use[i] = 0, cnt[i] = 0;
dis[s] = 0;
queue<int> q;
q.push(s);
int u,v,w;
while(! q.empty())
{
u = q.front(); q.pop();
use[u] = 0;
cnt[u] ++;
if(cnt[u] > n + 1 || dis[s] < 0) return false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
v = edge[i].v;
w = edge[i].w;
if(dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if(! use[v])
{
q.push(v);
use[v] = 1;
}
}
}
}
return true;
}
void init()
{
k = 0;
memset(head, -1, sizeof(head));
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
//freopen("T.txt","r",stdin);
cin >> n >> m;
init();
int tp;
int u,v,w;
for(int i = 1; i <= m; i ++)
{
cin >> tp;
if(tp == 1)
{
cin >> u >> v >> w;
Add(u, v, -w);
// u - v >= w
// v <= u - w
}
else if(tp == 2)
{
cin >> u >> v >> w;
// u - v <= w;
// u <= v + w;
Add(v, u, w);
}
else
{
cin >> u >> v;
Add(u, v, 0);
Add(v, u, 0);
}
}
for(int i = 1; i <= n; i ++)
Add(0, i, 0);
if(Spfa(0))
cout << "Yes\n";
else
cout << "No\n";
return 0;
}
题解二(Spfa dfs优化版)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 100005;
const int maxm = 200005;
struct Edge
{
int v,w,next;
} edge[maxm];
int head[maxn],used[maxn];
int dis[maxn];
int n,m;
int k = 0;
void Add(int u, int v, int w)
{
edge[++ k] = (Edge){ v, w, head[u]}; head[u] = k;
}
bool Spfa(int now)
{
used[now] = true; //做标记 标记节点 now 已经使用过了,在dfs中如果 若是再次走到这个 带标记的now 则代表图中有环
for(int i = head[now]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
int w = edge[i].w;
if(dis[v] > dis[now] + w)
{
if(used[v]) return false; //若是再次走到这个 带标记的v 则代表图中有环
dis[v] = dis[now] + w;
if(! Spfa(v)) return false;
}
}
used[now] = false;
return true;
}
void init()
{
for(int i = 0; i <= n; i ++)
head[i] = -1, dis[i] = INF;
dis[0] = 0;
k = 0;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
//freopen("T.txt","r",stdin);
cin >> m >> n;
init();
int tp;
for(int i = 1; i <= n; i ++)
{
cin >> tp;
int a,b,c;
if(tp == 1)
{
cin >> a >> b >> c;
// a - b >= c
// b <= a - c
Add(a, b, -c);
}
else if(tp == 2)
{
cin >> a >> b >> c;
// a - b <= c;
// a <= b + c;
Add(b, a, c);
}
else
{
cin >> a >> b;
Add(a, b, 0);
Add(b, a, 0);
}
}
for(int i = 1; i <= n; i ++)
Add(0, i, 0);
if(Spfa(0)) cout << "Yes\n";
else cout << "No\n";
return 0;
}