1. 程式人生 > 其它 >CF1253B - Silly Mistake(貪心+構造性演算法+提高階)

CF1253B - Silly Mistake(貪心+構造性演算法+提高階)

【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 成文