1 条题解
-
0
「POI2014 R1」砖块 Bricks 题解
题目大意
给定 k 种颜色的砖块,每种颜色有特定数量。已知原始砖块序列的第一个颜色是 p,最后一个颜色是 q,且任意相邻两个砖块颜色不同。要求构造一个满足条件的序列,或者判断无解。
解题思路
这是一个典型的构造问题,需要合理安排砖块的排列顺序,使得:
- 第一个砖块颜色为 p
- 最后一个砖块颜色为 q
- 相邻砖块颜色不同
- 每种颜色的砖块数量符合要求
关键观察
-
可行性判断:
- 如果某种颜色的砖块数量过多(超过总数的一半+1),则无解
- 如果首尾颜色相同但数量只有1个,则无解(因为需要放在首尾两个位置)
-
构造策略:
- 使用贪心+优先队列,每次选择剩余数量最多的颜色(且不与前一个颜色相同)
- 如果必须选择与前一颜色相同的颜色,则说明无解
算法流程
-
预处理:
- 将首尾颜色对应的砖块数量各减1
- 如果减后出现负数,则无解(特殊情况:k=1时允许首尾相同)
-
贪心构造:
- 使用大根堆维护各种颜色的剩余数量
- 每次选择剩余数量最多的颜色(且不与前一个颜色相同)
- 如果堆顶颜色与前一个相同,则选择次多的颜色
- 如果无法选择不同的颜色,则无解
-
收尾处理:
- 在序列末尾添加最后一个颜色 q
- 检查整个序列是否满足相邻不同的条件
代码实现
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll MOD = 1e9 + 7; const int N = 1e6 + 5; int k, p, q; int a[N]; struct node { int num, clo; }; struct cmp { bool operator()(node x, node y) { return x.num < y.num; } }; priority_queue<node, vector<node>, cmp> Q; vector<int> ans; int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); bool flag = 1; int sum = 0; cin >> k >> p >> q; for (int i = 1; i <= k; ++i) { cin >> a[i]; sum += a[i]; if (i == q) --a[i]; if (i == p) --a[i]; if (a[i] > 0) Q.push({a[i], i}); } // 检查首尾颜色数量是否足够 if (a[q] < 0 && k != 1) { cout << "0"; return 0; } // 初始放置第一个颜色 p ans.push_back(p); int last = p; while (!Q.empty()) { bool f = 0; node x = Q.top(); Q.pop(); // 确保取出的数据是最新的 while (!Q.empty() && x.num != a[x.clo]) { x = Q.top(); Q.pop(); } node y; // 如果堆顶颜色与上一个相同,需要选择次多的颜色 if (x.clo == last) { y = x; if (Q.empty()) { flag = 0; break; } f = 1; x = Q.top(); Q.pop(); while (!Q.empty() && x.num != a[x.clo]) { x = Q.top(); Q.pop(); } } // 如果当前颜色不是q且数量等于a[q],优先放置q if (x.clo != q && x.num == a[q] && q != last) { ans.push_back(q); last = q; a[q]--; Q.push(x); if (a[q]) Q.push({a[q], q}); } else { ans.push_back(x.clo); last = x.clo; x.num--; a[x.clo]--; if (x.num) Q.push(x); } if (f) Q.push(y); } // 在序列末尾添加最后一个颜色 q if (ans.size() < sum) ans.push_back(q); // 检查相邻颜色是否相同 for (int i = 0; i < ans.size() - 1; ++i) if (ans[i] == ans[i + 1]) flag = 0; if (!flag) { cout << "0"; return 0; } // 输出结果 for (int i = 0; i < ans.size(); ++i) { cout << ans[i]; if (i != ans.size() - 1) cout << " "; } return 0; }算法分析
时间复杂度
- 每个元素最多入队出队一次
- 时间复杂度:O(n log k),其中 n 是砖块总数,k 是颜色种类数
- 使用优先队列维护,每次操作 O(log k)
空间复杂度
- O(n + k),存储序列和优先队列
样例分析
样例1
输入:3 3 1 2 3 3 输出:3 2 1 3 2 3 2 1解释:颜色1有2个,颜色2有3个,颜色3有3个。首颜色为3,尾颜色为1。输出序列满足所有条件。
样例2
输入:3 3 1 2 4 2 输出:0解释:颜色2有4个,但总数为8,4 > (8+1)/2 = 4.5,数量过多导致无解。
总结
本题的关键在于:
- 贪心策略:优先选择剩余数量最多的颜色
- 可行性判断:检查颜色数量是否过多
- 边界处理:正确处理首尾颜色的特殊情况
这种贪心+优先队列的方法能够高效地构造出满足条件的序列,或者在无法构造时及时判断无解。
- 1
信息
- ID
- 5088
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者