CF1253B - Silly Mistake(貪心+構造性演算法+提高階)
阿新 • • 發佈:2021-12-20
【2021.12.15】隊內賽第一題
【2021.12.20】補題
)尋找某一元素在此前有沒有出現過,容易想到二分查詢,能用二分查詢的容器必然是有序的,這裡選用
CF1253B - Silly Mistake(源地址自⇔CF1253B)
目錄Problem
tag:
⇔貪心、⇔構造性演算法、⇔提高階(*1400)
題意:
(簡化版)對於給定的序列,要求將其分成滿足條件的若干段,條件如下:
- 如果在這一段中有 \(k\) ,則一定要有 \(-k\) 。
輸出分段的數量及每一段的元素數量;若不能恰好分割完,輸出 \(-1\) 。
思路:
(隊內賽自己, \(\mathcal O (n* log_2 n)\)
std::set
。分三種情況討論:
-
讀入正數 \(x\) ——若 \(x\) 不在容器中、 \(x\) 在這一分段裡尚未出現過(未被標記),則將 \(x\) 加入容器。
-
讀入負數 \(-x\) ——若 \(x\) 已在容器中,則刪除 \(x\) 。
-
其他所有情況均不滿足條件。
每次容器為空時,即代表一個分段,記錄並將標記陣列清空。
(官方,\(\mathcal O(n)\) )優化二分查詢為陣列直接記錄,優化標記陣列為 std::map
(因為涉及到多次清空操作,普通陣列不能勝任該工作)即可。
使用變數 len
記錄分段所含元素的數量,當其為 \(0\) 時,即代表一個分段。
AC程式碼1(隊內賽自己):
//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; }
AC程式碼2(官方思路,虛擬碼)
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 成文