题解:AcWing 1169 糖果
【题目来源】
AcWing:1169 糖果 - AcWing题库
【题目描述】
幼儿园里有 \(N\) 个小朋友,老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。
但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,老师需要满足小朋友们的 \(K\) 个要求。
幼儿园的糖果总是有限的,老师想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
【输入】
输入的第一行是两个整数 \(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\) 个小朋友分到的糖果。
小朋友编号从 \(1\) 到 \(N\)。
【输出】
输出一行,表示老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 \(-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
【核心思想】
-
问题分析:给定 \(N\) 个小朋友和 \(K\) 个约束条件,每个约束定义了两个小朋友糖果数量的关系(等于、小于、大于、不少于、不多于)。需要求满足所有约束的最小糖果总数,若无法满足则输出 \(-1\)。这是一个差分约束系统问题,可以转化为图论中的最长路径问题求解。
-
算法选择:
- 差分约束系统:将约束条件转化为不等式 \(x_j - x_i \geq c\),构建有向图,求最长路径
- SPFA 算法求最长路径:使用栈优化版本,检测正环(即差分约束系统无解)
- 超级源点:添加虚拟源点 \(0\),向所有节点连边权为 \(1\) 的边,确保每个小朋友至少分到 \(1\) 个糖果
- 计数数组 \(cnt[]\):记录节点被松弛的次数,超过 \(n+1\) 次说明存在正环
-
关键步骤:
- 约束转化(将5种约束转化为不等式):
- \(X=1\)(\(a = b\)):\(a \geq b\) 且 \(b \geq a\),添加边 \(b \to a\)(权0)和 \(a \to b\)(权0)
- \(X=2\)(\(a < b\)):\(b \geq a + 1\),添加边 \(a \to b\)(权1)
- \(X=3\)(\(a \geq b\)):\(a \geq b\),添加边 \(b \to a\)(权0)
- \(X=4\)(\(a > b\)):\(a \geq b + 1\),添加边 \(b \to a\)(权1)
- \(X=5\)(\(b \geq a\)):\(b \geq a\),添加边 \(a \to b\)(权0)
- 添加超级源点:对于 \(i\) 从 \(1\) 到 \(n\),添加边 \(0 \to i\)(权1),确保 \(dist[i] \geq 1\)
- SPFA 求最长路径:
- 初始化 \(dist[i] = -INF\),\(dist[0] = 0\)
- 使用栈存储待松弛节点,超级源点 \(0\) 入栈
- 当栈非空时,取出栈顶节点 \(t\),松弛其所有邻接边
- 若 \(dist[j] < dist[t] + w\),更新 \(dist[j]\),\(cnt[j] = cnt[t] + 1\)
- 若 \(cnt[j] \geq n+1\),说明存在正环,返回无解
- 计算答案:若存在正环输出 \(-1\),否则输出 \(\sum_{i=1}^{n} dist[i]\)
- 约束转化(将5种约束转化为不等式):
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot m)\),SPFA 最坏情况,其中 \(m\) 为约束转化后的边数(最多 \(2K + n\) 条边)
- 空间复杂度:\(O(n + m)\),邻接表存储图
- 适用于差分约束系统、不等式组求解问题
-
差分约束系统的核心思想:
- 不等式转化:将约束条件统一转化为 \(x_j - x_i \geq c\) 的形式,对应图中的边 \(i \to j\)(权 \(c\))
- 最长路径建模:求满足所有不等式的最小解,等价于求从超级源点出发的最长路径
- 正环检测:若图中存在正环,则对应的不等式组无解(可以无限增大)
- 超级源点技巧:通过添加虚拟源点,将多个起点问题转化为单源问题
- 最小解性质:最长路径的解是满足所有约束的最小解
- 适用于不等式组求解、资源分配、调度问题等场景
【解题思路】


【算法标签】
差分约束
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 使用长整型
const int N = 100005, M = N * 3; // 最大顶点数和边数
int n, m; // n: 顶点数, m: 约束数量
int h[N], e[M], ne[M], w[M], idx; // 链式前向星存储图
int q[N], dist[N]; // 队列和距离数组
int cnt[N], st[N]; // 松弛计数和顶点是否在栈中
/**
* 添加有向边
* @param a 起点
* @param b 终点
* @param c 权重
*/
void add(int a, int b, int c)
{
e[idx] = b; // 边指向的顶点
w[idx] = c; // 边的权重
ne[idx] = h[a]; // 指向原链表头
h[a] = idx++; // 更新头指针
}
/**
* SPFA算法求最长路径,并检测正环
* 使用栈实现,适合检测正环
* @return 存在正环返回false,否则返回true
*/
bool spfa()
{
stack<int> q; // 使用栈而不是队列,更易检测正环
// 初始化距离为负无穷
memset(dist, -0x3f, sizeof(dist));
dist[0] = 0; // 超级源点距离为0
q.push(0); // 超级源点入栈
st[0] = true; // 标记在栈中
while (!q.empty())
{
int t = q.top(); // 取出栈顶
q.pop();
st[t] = false; // 标记不在栈中
// 遍历t的所有邻接边
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i]; // 邻接顶点
// 松弛操作:求最长路径
if (dist[j] < dist[t] + w[i])
{
dist[j] = dist[t] + w[i]; // 更新最长距离
cnt[j] = cnt[t] + 1; // 松弛次数+1
// 如果顶点j被松弛了n+1次,说明存在正环
if (cnt[j] >= n + 1) // 注意是n+1,因为有超级源点
{
return false; // 存在正环
}
// 如果j不在栈中,入栈
if (!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
return true; // 不存在正环
}
signed main() // 因为使用了#define int long long
{
// 输入顶点数和约束数量
cin >> n >> m;
// 初始化邻接表
memset(h, -1, sizeof(h));
// 处理m个约束
while (m--)
{
int x, a, b;
cin >> x >> a >> b;
// 根据约束类型添加边
if (x == 1) // a = b
{
add(b, a, 0); // a ≥ b
add(a, b, 0); // b ≥ a
}
else if (x == 2) // a < b
{
add(a, b, 1); // b ≥ a + 1
}
else if (x == 3) // a ≥ b
{
add(b, a, 0); // a ≥ b
}
else if (x == 4) // a > b
{
add(b, a, 1); // a ≥ b + 1
}
else // x == 5: b ≥ a
{
add(a, b, 0); // b ≥ a
}
}
// 添加超级源点到所有顶点的边
for (int i = 1; i <= n; i++)
{
add(0, i, 1); // 每个顶点至少分配1
}
// 执行SPFA,检测是否存在正环
if (!spfa())
{
puts("-1"); // 存在正环,无解
}
else
{
// 计算所有顶点的距离和
int res = 0;
for (int i = 1; i <= n; i++)
{
res += dist[i];
}
cout << res << endl;
}
return 0;
}
【运行结果】
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
11
浙公网安备 33010602011771号