⇔贪心、⇔构造性算法、⇔提高级(*1400)
(简化版)对于给定的序列,要求将其分成满足条件的若干段,条件如下:
输出分段的数量及每一段的元素数量;若不能恰好分割完,输出 \(-1\) 。
(队内赛自己, \(\mathcal O (n* log_2 n)\) )寻找某一元素在此前有没有出现过,容易想到二分查找,能用二分查找的容器必然是有序的,这里选用 std::set
。分三种情况讨论:
读入正数 \(x\) ——若 \(x\) 不在容器中、 \(x\) 在这一分段里尚未出现过(未被标记),则将 \(x\) 加入容器。
读入负数 \(-x\) ——若 \(x\) 已在容器中,则删除 \(x\) 。
其他所有情况均不满足条件。
每次容器为空时,即代表一个分段,记录并将标记数组清空。
(官方,\(\mathcal O(n)\) )优化二分查找为数组直接记录,优化标记数组为 std::map
(因为涉及到多次清空操作,普通数组不能胜任该工作)即可。
使用变量 len
记录分段所含元素的数量,当其为 \(0\) 时,即代表一个分段。
//A WIDA Project #include <bits/stdc++.h> using namespace std; #define LL long long LL n, num[1000050], x, pre; bool Ans = true; vector<int> ans; set<int> s, v; int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n; for(int i = 1; i <= n; i ++) { cin >> x; if(x > 0) { if(s.find(x) == s.end() && v.find(x) == v.end()) //里面没有 && 第一次放入 s.emplace(x), v.emplace(x); else Ans = false; }else { if(x < 0 && v.find(-x) != v.end()) //负数,里面有 s.erase(-x); else Ans = false; } if(s.empty()) { v.clear(); ans.push_back(i - pre); pre = i; } } if(Ans == false || (int)ans.size() == 0 || !s.empty()) cout << "-1\n"; else { cout << ans.size() << "\n"; for(auto i : ans) cout << i << " "; } return 0; }
LL n, ans, num[MAX], x, pre, len, s[MAX]; bool Ans = true; vector<int> ans; map<int, int> v; int main() { cin >> n; FOR(i, 1, n) { cin >> x; if(x > 0) { if(s[x] == 0 && v[x] == 0) s[x] ++, v[x] ++, len ++; else Ans = false; }else { if(s[-x] == 1) s[-x] --, len --; else Ans = false; } if(len == 0) { v.clear(); ans.push_back(i - pre); pre = i; } } if(Ans == false || sz(ans) == 0 || len != 0) P(-1); else { P(ans.size()); For(i, ans) cout << i << " "; } return 0; }
(补题1)忘记判断结束时容器是否为空,若有数据则也是 \(-1\) 。WA。
文 / WIDA
2021.12.20 成文
首发于WIDA个人博客,仅供学习讨论
更新日记:
2021.12.20 成文