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 后面了,是同类型的题。
浙公网安备 33010602011771号