AtCoder Beginner Contest 352題解

fantasy_0721發表於2024-05-05

AtCoder Beginner Contest 352

Time : 2024-05-04(Sat) 20:00 - 2024-05-04(Sat) 21:40

A AtCoder Line

問題陳述

img

輸入

輸入內容由標準輸入法提供,格式如下

N X Y Z

題解

題意為檢查Z是否處於X與Y之間

需根據X與Y的大小來判斷乘坐進站列車還是出站列車

void solve() {
    cin >> n >> x >> y >> z;
    if(x > y)swap(x, y);
    cout << (x <= z && z <= y ? "Yes" : "No") << endl;
}

B Typing

問題陳述

img

題解

S為理想輸入, T為實際輸入

題意為在T與S的字串匹配, 每次輸入S在T中的下標

演算法 : 雙指標

void solve() {
    string s, t;
    cin >> s >> t;
    for(int i = 0, j = 0; i < t.size() ; i++) {
        if(s[j] == t[i])cout << i + 1 << " ", j++;
    }
}

C Standing On The Shoulders

問題陳述

img

題解

總高度為N個巨人的肩高和加上最上方巨人的頭高-肩高

總高度最大即最上方巨人頭肩高差最大

遍歷得最大值即可, 記得開long long

void solve() {
    cin >> n;
    int sd, hd, mx = -1;
    for(int i = 0; i < n; i++) {
        cin >> sd >> hd;
        res += sd;
        mx = max(mx, hd - sd);
    }
    cout << res + mx << endl;
}

D

問題陳述

img

題解

題意有點繞人, 簡單來說是在 1 ~ N 的排列中找到長度為 k 的下標集合, 使得該下標集合對應的排列集合排序後為k長度的連續序列(a , a + 1 , a + 2 ... a + k - 1), 找到滿足該條件下標集合的極差的最小值

一開始半天沒讀明白題, 覺得答案有單調性決定用二分答案加上滑動視窗來做, 思路不會最佳化TLE了

看到jiangly大佬有一個很巧妙的做法, 下標和排列P都是1 ~ N, 可以使用陣列一一對應儲存

使用set維護一個 k 長度的滑動視窗, 遍歷 a 來尋找最小值, 具體見程式碼註釋

void solve() {
    cin >> n >> k;
    for(int i = 1; i <= n; i++) {
        cin >> t;
        p[t] = i; //儲存下標
    }
    
    set<int> s;//set集合有序儲存滑動視窗中的下標
    for(int i = 1; i <= n; i++) { //列舉整數a
        s.insert(p[i]);
        if(i > k)s.erase(p[i - k]); //滑動視窗
        if(i >= k)
            ans = min(ans, *s.rbegin() - *s.begin()); //維護最大下標
    }
    cout << ans;
}

E Clique Connect

問題陳述

img

題解

很裸的一道求最小生成樹題

稠密圖, 選用Kruskal演算法來完成

題目給定邊的時候, 由於邊權相等, 可以把給定集合看作一整個連通塊, 便只相當於插入了k-1條邊

int n, m, k, c, a, root, cnt;
int ans;
int p[N];//並查集維護節點的父節點

struct E {
    int a, b, c;
    bool operator < (const E& C) {
        return c < C.c;
    }
} edgs[N * 2];

int find(int u) {
    if(u != p[u])p[u] = find(p[u]);
    return p[u];
}

int kruskal() {
    for(int i = 1; i <= n; i++)p[i] = i;//初始化
    int num = 0;//儲存最小生成樹邊的數目
    sort(edgs, edgs + cnt);
    for(int i = 0; i < cnt; i++) { //列舉edgs
        int a = edgs[i].a, b = edgs[i].b, c = edgs[i].c;
        if(find(a) != find(b)) { //如果兩個節點沒有聯通(所在連通塊根節點不同)
            p[find(a)] = find(b);
            ans += c;
            num++;
        }
    }
    return num;
}

void solve() {
    cin >> n >> m;
    while(m--) {
        cin >> k >> c;//題意即給定含有k個節點的連通塊
        for(int i = 0; i < k; i++) {
            cin >> a;
            if(i > 0)
                edgs[cnt ++ ] = {root, a, c};//cnt為邊的數量
            root = a;//以第一個節點為該連通塊根節點
        }
    }
    if(kruskal() == n - 1)
        cout << ans << endl;
    else
        cout << -1 << endl;

}

F,G 等待更新...

相關文章