[洛谷P1993]小K的农场
原题传送门
这道题的实质是差分约束。我们逐个分析。
- 农场a比农场b至少多种植了c个单位的作物,
- 农场a比农场b至多多种植了c个单位的作物,
- 农场a与农场b种植的作物数一样多。
设$a$农场中有$d[a]$个单位植物,$b$农场为$d[b]$,
对于第一点,则满足:$d[a]-d[b] \geq c$,变形下就变成:$d[b]-d[a] \leq -c$。
对于第二点:$d[a]-d[b] \leq c$。
对于第三点:$d[a]=d[b]$,即$d[a]-d[b]=0$,变成不等式就要同时满足$d[a]-d[b] \leq 0$且$d[b]-d[a] \leq 0$。
根据一系列不等式组,即可构图。
对于$d[u]-d[v] \leq w$,将$v$连一条有向边到$u$,权值为$w$。
最后将$0$号结点连向每个结点,权值为$0$。
判断整个图是否有负环即可。
因为如果有负环,算法将会无限制地执行松弛操作,也就是无法满足不等式组成立,也就是输出$"No"$。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define re register 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i) 7 #define repd(i, a, b) for (re int i = a; i >= b; --i) 8 #define maxx(a, b) a = max(a, b); 9 #define minn(a, b) a = min(a, b); 10 #define LL long long 11 #define inf (1 << 30) 12 13 inline int read() { 14 int w = 0, f = 1; char c = getchar(); 15 while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar(); 16 while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar(); 17 return w * f; 18 } 19 20 const int maxn = 1e4 + 5, maxm = 1e4 + 5; 21 22 struct Edge { 23 int u, v, w, pre; 24 }; 25 26 queue<int> q; 27 int inq[maxn], cnt[maxn]; 28 struct Graph { 29 Edge edges[maxm << 2]; 30 int n, m; 31 int G[maxn], dis[maxn]; 32 void init(int n) { 33 this->n = n; 34 m = 0; 35 memset(G, 0, sizeof(G)); 36 } 37 void AddEdge(int u, int v, int w) { 38 edges[++m] = (Edge){u, v, w, G[u]}; 39 G[u] = m; 40 } 41 bool SPFA() { 42 memset(dis, 0x3f, sizeof(dis)); 43 memset(cnt, 0, sizeof(cnt)); 44 memset(inq, 0, sizeof(inq)); 45 //rep(i, 0, n) { q.push(i); inq[i] = 1; dis[i] = 0; } 46 q.push(0), inq[0] = 1, dis[0] = 0; 47 while (!q.empty()) { 48 int u = q.front(); q.pop(); inq[u] = 0; 49 for (int i = G[u]; i; i = edges[i].pre) { 50 Edge &e = edges[i]; 51 if (dis[e.u] + e.w < dis[e.v]) { 52 dis[e.v] = dis[e.u] + e.w; 53 if (!inq[e.v]) { 54 q.push(e.v); inq[e.v] = 1; 55 if (++cnt[e.v] > n) return false; 56 } 57 } 58 } 59 } 60 return true; 61 } 62 } G; 63 64 int n, m; 65 66 int main() { 67 n = read(), m = read(); 68 G.init(n); 69 70 rep(kase, 1, m) { 71 int opt = read(); 72 if (opt == 1) { 73 int u = read(), v = read(), w = read(); 74 G.AddEdge(u, v, -w); 75 } else 76 if (opt == 2) { 77 int u = read(), v = read(), w = read(); 78 G.AddEdge(v, u, w); 79 } else { 80 int u = read(), v = read(); 81 G.AddEdge(u, v, 0); 82 G.AddEdge(v, u, 0); 83 } 84 } 85 rep(i, 1, n) G.AddEdge(0, i, 0); 86 87 if (G.SPFA()) printf("Yes"); 88 else printf("No"); 89 90 return 0; 91 }
该做法在本题中会$TLE$,复杂度为$O(nm)$。
下面的方法把$SPFA$改成了深搜的写法,实质类似于将负权边构成图,单独判断是否有环。经验证,比上面快几十倍。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define re register 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i) 7 #define repd(i, a, b) for (re int i = a; i >= b; --i) 8 #define maxx(a, b) a = max(a, b); 9 #define minn(a, b) a = min(a, b); 10 #define LL long long 11 #define inf (1 << 30) 12 13 inline int read() { 14 int w = 0, f = 1; char c = getchar(); 15 while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar(); 16 while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar(); 17 return w * f; 18 } 19 20 const int maxn = 1e4 + 5, maxm = 1e4 + 5; 21 22 struct Edge { 23 int u, v, w, pre; 24 }; 25 26 struct Graph { 27 Edge edges[maxm << 1]; 28 int n, m; 29 int G[maxn], dis[maxn], vis[maxn]; 30 void init(int n) { 31 this->n = n+1; 32 m = 0; 33 memset(G, 0, sizeof(G)); 34 } 35 void AddEdge(int u, int v, int w) { 36 edges[++m] = (Edge){u, v, w, G[u]}; 37 G[u] = m; 38 } 39 bool spfa(int u) { 40 vis[u] = 1; 41 for (int i = G[u]; i; i = edges[i].pre) { 42 Edge &e = edges[i]; 43 if (dis[u] + e.w > dis[e.v]) { 44 dis[e.v] = dis[u] + e.w; 45 if (vis[e.v]) return false; 46 if (!spfa(e.v)) return false; 47 } 48 } 49 vis[u] = 0; 50 return true; 51 } 52 } G; 53 54 int n, m; 55 56 int main() { 57 n = read(), m = read(); 58 G.init(n); 59 60 rep(kase, 1, m) { 61 int opt = read(); 62 if (opt == 1) { 63 int u = read(), v = read(), w = read(); 64 G.AddEdge(u, v, w); 65 } else 66 if (opt == 2) { 67 int u = read(), v = read(), w = read(); 68 G.AddEdge(u, v, 0); 69 G.AddEdge(v, u, -w); 70 } else { 71 int u = read(), v = read(); 72 G.AddEdge(u, v, 0); 73 G.AddEdge(v, u, 0); 74 } 75 } 76 rep(i, 1, n) G.AddEdge(0, i, 0); 77 memset(G.dis, ~0x3f, sizeof(G.dis)); 78 G.dis[0] = 0; 79 if (G.spfa(0)) printf("Yes"); 80 else printf("No"); 81 82 return 0; 83 }