zoukankan      html  css  js  c++  java
  • AtCoder Beginner Contest 170 (D~F题,D筛法,E multiset使用,F Dijkstra算法改进)

    题目链接:Here

    ABC水题,

    D. Not Divisible

    看了题解才想到,可以用 Sieve of Eratosthenes,因为 (A_i) 最大才 (10^6)

    但有注意的点

    1
    1
    5
    2 2 2 3 3
    5
    2 2 2 4 4
    5
    1 1 1 1 2
    

    重复出现的数字

    简单的说:找出数列中不是其他数的倍数的数的个数(拗口)。用小数筛大数。

    const int N = 1e6 + 10;
    int a[N], cnt[N];
    bool st[N];
    int main() {
        cin.tie(nullptr)->sync_with_stdio(false);
        int n; cin >> n;
        for (int i = 1; i <= n; ++i) cin >> a[i], cnt[a[i]] += 1;
        for (int i = 1; i <= N; ++i) {
            if (cnt[i]) {
                if (cnt[i] > 1) st[i] = 1; // 对于重复出现的数据也要标记
                for (int j = i + i; j <= N; j += i) st[j] = 1;
            }
        }
        int ans = 0;
        for (int i = 1; i <= n; ++i) ans += !st[a[i]];
        cout << ans;
    }
    

    E. Smart Infants

    照着题目模拟就是,问题主要是要用对资料结构。 对于每个 kindergarden 我们需要一个可以高速插入、高速删除、高速查询最大值的容器。

    C++ 的 multiset 符合所需。 使用 c++ 的 multiset 要注意的一点是 set.erase(val) 会删除容器中所有值为 valentries,若只想删除一个值为valentry,要用 set.erase(set.find(val))

    即:对每次变换求所有集合最大值中的最小值。用s[]表示各个集合,m表示最大值集合,每次对集合元素删除加入。注意元素有可能相同,所以要用multiset.

    int main() { // try new nameing rules and Coding style
        cin.tie(nullptr)->sync_with_stdio(false);
        int N, Q;
        int M = 2e5;
        cin >> N >> Q;
    
        auto belong = vector<int>(N, -1);
        auto rating = vector<int>(N, -1);
        auto evenesses = multiset<int>();
        auto kdrgrtns = vector<multiset<int>>(M);
    
        for (int i = 0; i < N; ++i) {
            int A, B;
            cin >> A >> B;
            B--;
            rating[i] = A;
            belong[i] = B;
            kdrgrtns[B].insert(A);
        }
        for (auto k : kdrgrtns) {
            if (!k.empty())
                evenesses.insert(*k.rbegin());
        }
    
        while (Q--) {
            int C, D;
            cin >> C >> D;
            C--, D--;
    
            auto &kdrgrtn_src = kdrgrtns[belong[C]];
            auto &kdrgrtn_dst = kdrgrtns[D];
    
            evenesses.erase(evenesses.find(*kdrgrtn_src.rbegin()));
            kdrgrtn_src.erase(kdrgrtn_src.find(rating[C]));
            if (kdrgrtn_src.size() > 0)
                evenesses.insert(*kdrgrtn_src.rbegin());
    
            if (kdrgrtn_dst.size() > 0)
                evenesses.erase(evenesses.find(*kdrgrtn_dst.rbegin()));
            kdrgrtn_dst.insert(rating[C]);
            evenesses.insert(*kdrgrtn_dst.rbegin());
    
            belong[C] = D;
    
            cout << *evenesses.begin() << "
    ";
        }
    }
    

    F. Pond Skater

    Snuke,水上平衡车,住在一个矩形池塘,可以看成 H 列 W 行,(i, j) 表示第 i 列第 j 行。池塘里长着荷叶,荷叶是不能进入的。如果 cij 是 @,表示荷叶。如果 cij 是 .,表示不是荷叶。

    Snuke 每次可以向北、东、南、西的任意同一个方向移动一步到 K 步,但是不能通过荷叶,同时也不能移动到池塘外。

    给我们起点坐标 (x1, y1) 和终点坐标 (x2, y2),要求我们找到最小的移动步数。如果不能到达,输出 -1。


    Here 有一个博主写了很详细的分析

    这题马上让人想到是最短路径,于是就刻了一个 Dijkstra,然后就会 TLE。

    对于每个 vertex 我们都展开 4 * K 条边实在太多了,所幸我们可以剪枝。

    当我们在 (r, c),尝试用 dis[r][c] + 1 松弛 dis[r + k * dr][c + k * dc] 不成功时,我们不需要展开更大的 k,因为那些更远的点(即 k 更大)不可能透过目前的路径得到更佳的解。

    写成程式码就是在展开边的地方加入剪枝:

    // omit
    for (int k = 1; k <= K; k++) {
        // omit
        if (dis[r + k * dr][c + k * dc] < dis[r][c] + 1) break;
        // omit
    }
    

    你就能 AC 了。官方題解我是沒有看懂,這題網路上的題解都是 BFS + 剪枝,我是覺得挺神奇的啦,最短路徑不是應該想到 Dijkstra 嗎?還有人說此剪枝方法不適用 Dijkstra,只能用在 BFS 上 (┛ಠ_ಠ)┛彡┻━┻

    using ti3 =  tuple<int, int, int>;
    const int inf = 0x3f3f3f3f;
    int main() {
        cin.tie(nullptr)->sync_with_stdio(false);
        int H, W, K;
        cin >> H >> W >> K;
        int sr, sc, tr, tc;
        cin >> sr >> sc >> tr >> tc;
        sr--, sc--, tr--, tc--;
        vector<string> S(H);
        for (int i = 0; i < H; ++i) cin >> S[i];
    
        vector<ti3> nxt;
        nxt.push_back({+1, 0, 1});
        nxt.push_back({-1, 0, 1});
        nxt.push_back({0, +1, 1});
        nxt.push_back({0, -1, 1});
    
        vector<vector<int>> dis(H, vector<int>(W, inf));
        auto q = priority_queue<ti3, vector<ti3>, greater<ti3>>();
    
        dis[sr][sc] = 0;
        q.push({0, sr, sc});
    
        while (!q.empty()) {
            auto [d, r, c] = q.top(); q.pop();
            if (d > dis[r][c]) continue;
            for (auto &&[dr, dc, w] : nxt) {
                for (int k = 1; k <= K; ++k) {
                    int nr = r + k * dr;
                    int nc = c + k * dc;
                    if (nr < 0 || nr >= H) break;
                    if (nc < 0 || nc >= W) break;
                    if (S[nr][nc] == '@') break;
                    if (dis[nr][nc] < d + w) break;
                    if (dis[nr][nc] > d + w) {
                        dis[nr][nc] = d + w;
                        q.push({dis[nr][nc], nr, nc});
                    }
                }
            }
        }
    
        cout << (dis[tr][tc] == inf ? -1 : dis[tr][tc]);
    }
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    C语言指向指针的指针
    C语言注意事项
    C语言指针
    C语言字符串
    C语言数组
    C语言交换两个数的值
    C语言位运算符
    C语言各种进制输出
    C语言中各种进制的表示
    C 语言sizeof运算符
  • 原文地址:https://www.cnblogs.com/RioTian/p/15152417.html
Copyright © 2011-2022 走看看