AtCoder Beginner Contest 007

A

题意

\(N\) 个点,每两个点严格被一条边相连,且不成环。询问边数。

题解

边数是 \(N - 1\)

B

题意

给一个字符串 \(S\) ,构造一个字符串 \(T \ (1 \leq |T| \leq 100)\) 。使得 \(T\) 的字典序比 \(S\) 小。

题解

那么 \(T = a\) 就是最小的字典序,当且仅当 \(S = a\) 时不会比 \(S\) 更小。

    std::string s; std::cin >> s;
    if (s == "a") std::cout << -1 << "\n";
    else std::cout << "a" << '\n';

C

题意

给一个 \(n \times m\) 的迷宫,"." 表示空地,"#" 表示墙,你可以从一块空地到达另一块与之有相邻边的空地。给定起点和终点坐标,询问最少步数。

题解

搜索的时间复杂度是 \(O(nm)\) ,但实际最短路径搜索中是 \(bfs\) 更快。找到最短路径包含的点数,然后输出“点数-1”,即输出边数。

    int N, M; std::cin >> N >> M;
    int sx, sy; std::cin >> sx >> sy;
    int tx, ty; std::cin >> tx >> ty;
    std::vector<std::string > G(N + 1);
    for (int i = 1; i <= N; i++) {
        std::cin >> G[i]; G[i].insert(0, " ");
    }
    auto out = [&] (int x, int y) -> bool {
        return x < 1 || N < x || y < 1 || M < y;
    };
    auto forbid = [&] (int x, int y) -> bool {
        return G[x][y] == '#';
    };
    std::vector<std::array<int, 2> > dir{{1, 0}, {0, -1}, {-1, 0}, {0, 1}};
    std::queue<std::array<int, 2> > que;
    std::vector<std::vector<int> > dist(N + 1, std::vector<int>(M + 1, -1));
    que.push({sx, sy});
    dist[sx][sy] = 1;
    while (!que.empty()) {
        int x = que.front()[0], y = que.front()[1];
        que.pop();
        for (auto d : dir) {
            int nx = x + d[0], ny = y + d[1];
            if (out(nx, ny) || forbid(nx, ny)) continue;
            if (dist[nx][ny] != -1) continue;
            dist[nx][ny] = dist[x][y] + 1;
            que.push({nx, ny});
        }
    }
    if (dist[tx][ty] != -1) dist[tx][ty] -= 1;
    std::cout << dist[tx][ty] << "\n";

D

diffculty 1718

题意

给两个正数 \(A, B\) 满足 \(1 \leq A \leq B \leq 10^{18}\) 。询问区间 \([A, B]\) 中有多少数字满足存在某一位是 \(4\)\(9\)

题解

atcoder 上第一道数位 DP 。

经典的 \(A \cup B = 1 - (\neg A \cap \neg B)\)

先计算不存在 \(4\) 且不存在 \(9\)\(\leq N\) 的数总共有多少个,定义为 \(calc(N)\)

这个计数方式可以采取数位 DP 。 f[考虑到了第 pos 位][前 pos - 1 位未满] 总共有多少位。

不妨直接记忆化搜索,因为 \(pos, true\) 往后和计数方式一样,\(pos, false\) 往后计数方式一样。所以重复状态重复计数的答案一样。
总时间复杂度 \(T(\sum pos \times 2) = O(length)\)

于是 \(calc(B) - calc(A - 1)\)\([A, B]\) 中不存在 \(4\) 且不存在 \(9\) 的数字个数。
于是 \((B - A + 1) - (calc(B) - calc(A - 1))\)\([A, B]\) 中存在 \(4\)\(9\) 的数字个数。

view
    i64 L, R; std::cin >> L >> R;
    auto calc = [&] (i64 n) -> i64 {
        std::string s = std::to_string(n);
        int m = s.size();
        std::vector<std::vector<i64> > f(m, std::vector<i64>(2, -1));
        std::function<i64(int, int)> dfs = [&] (int pos, int smaller) -> i64 {
            if (pos == m) { return 1; } // 搜完了,返回这个数
            i64 &res = f[pos][smaller];
            if (res != -1) return res; // 历史版本存在则直接返回
            res = 0; // 否则开始搜索历史版本
            int up = smaller ? 9 : (s[pos] - '0'); // 根据是否未满确定上界
            for (int i = 0; i <= up; i++) {
                if (i == 4 || i == 9) continue; // 跳过 4 和 9 的数位
                res += dfs(pos + 1, smaller | (i < up)); // 递归向下
            }
            return res;
        };
        dfs(0, 0); // 第 0 位开始搜索,第 0 位显然不会未满于是 false
        return f[0][0];
    };
    std::cout << (R - L + 1) - (calc(R) - calc(L - 1)) << "\n";

附加题。
其实写在 ABC 006 A 后面了,是同类型的题。

posted @ 2024-07-29 19:17  03Goose  阅读(19)  评论(0)    收藏  举报