NC20284 [SCOI2011]糖果
题目
题目描述
幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww需要满足小朋友们的K个要求。幼儿园的糖果总是有限的,lxhgww想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
输入描述
输入的第一行是两个整数N,K。
接下来K行,表示这些点需要满足的关系,每行3个数字,X,A,B。
如果X=1, 表示第A个小朋友分到的糖果必须和第B个小朋友分到的糖果一样多;
如果X=2, 表示第A个小朋友分到的糖果必须少于第B个小朋友分到的糖果;
如果X=3, 表示第A个小朋友分到的糖果必须不少于第B个小朋友分到的糖果;
如果X=4, 表示第A个小朋友分到的糖果必须多于第B个小朋友分到的糖果;
如果X=5, 表示第A个小朋友分到的糖果必须不多于第B个小朋友分到的糖果;
输出描述
输出一行,表示lxhgww老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出-1。
示例1
输入
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
输出
11
备注
对于30%的数据,保证 \(N\leq100\)
对于100%的数据,保证 \(N\leq100000\)
对于所有的数据,保证 \(K\leq100000, 1\leq X\leq5, 1\leq A, B\leq N\)
题解
知识点:差分约束。
这是一道差分约束模板题。
求最小值用最长路。关于建边利用不等式:\(u\to v\) 权为 \(w\) ,则 \(dis[u]\geq dis[v]+w\) 。
如果存在正环,则无解。
时间复杂度 \(O(n+km)\)
空间复杂度 \(O(n+m)\)
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100007, K = 100007;
template<class T>
struct Graph {
struct edge {
int v, nxt;
T w;
};
int idx;
vector<int> h;
vector<edge> e;
Graph(int n, int m) :idx(0), h(n + 1), e(m + 1) {}
void add(int u, int v, T w) {
e[++idx] = edge{ v,h[u],w };
h[u] = idx;
}
};
Graph <int> g(N, 3 * K);///一号坑,超级源点连边多了N个,原本边可能全是等于有2K个
int n, k;
bool vis[N];
int dis[N], cnt[N];
stack<int> q;///二号坑,queue会被卡入点顺序
bool SPFA(int s) {
memset(dis, -1, sizeof(dis));
q.push(s);
dis[s] = 0;
vis[s] = 1;
while (!q.empty()) {
int u = q.top();
q.pop();
vis[u] = 0;
for (int i = g.h[u];i;i = g.e[i].nxt) {
int v = g.e[i].v;
int w = g.e[i].w;
if (dis[v] < dis[u] + w) {
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1;
if (cnt[v] >= n + 1) return false;///三号坑,多了个超级源点,所以n+1
if (!vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
}
return true;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1;i <= k;i++) {
int x, a, b;
cin >> x >> a >> b;
if (x == 1) {
g.add(a, b, 0);
g.add(b, a, 0);
}
else if (x == 2) {
g.add(a, b, 1);
}
else if (x == 3) {
g.add(b, a, 0);
}
else if (x == 4) {
g.add(b, a, 1);
}
else if (x == 5) {
g.add(a, b, 0);
}
}
for (int i = 1;i <= n;i++) g.add(n + 1, i, 1);///超级源点,为了方便,距离为0,到其他点为1,表示其他点>=0+1
/// 正边最长路用SPFA,A->B == dis[A] + w <= dis[B]
if (!SPFA(n + 1)) cout << -1 << '\n';
else {
ll ans = 0;///四号坑,最大值n^2/2
for (int i = 1;i <= n;i++) ans += dis[i];
cout << ans << '\n';
}
return 0;
}
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17028735.html