AtCoderABC387题解
A题
链接
题目大意:给你AB,求,直接上代码!
#include<iostream>
using namespace std;
signed main() {
int a,b;
cin>>a>>b;
cout<<(a+b)*(a+b);
return 0;
}
B题
链接
给你一个列表,第行第
列的值是
,满足
,让你求这个列表中不等于
的数字之和。
因为数很小,根本不用优化,上代码!!!
#include<iostream>
using namespace std;
#define fo(i,begin,end) for(int i=begin;i<=end;i++)
signed main() {
int n;
cin>>n;
int ans=0;
fo(i,1,9) {
fo(j,1,9) {
if(i*j!=n){
ans+=i*j;
}
}
}
cout<<ans;
return 0;
}
C题
链接
有点难:
给你区间,求蛇形数的数量。
当区间 [L, R]
非常大时,直接模拟遍历区间内的每个数来判断是否为蛇形数会导致时间复杂度很高,可能会超时。我们可以使用数位 DP(动态规划)的方法来解决这个问题。
数位 DP 是一种针对数字的每一位进行动态规划的算法,通常用于解决与数字的数位特征相关的计数问题。对于本题,我们可以通过数位 DP 来计算小于等于某个数 x
的蛇形数的数量,然后用小于等于 R
的蛇形数数量减去小于等于 L - 1
的蛇形数数量,就可以得到区间 [L, R]
内的蛇形数数量。
上代码!!!
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
using namespace std;
// 数位DP数组,dp[pos][limit][first][maxDigit] 表示处理到第pos位,是否受到上界限制,是否是首位,之前的最大数字
long long dp[20][2][2][10];
// 数位DP函数
long long dfs(int pos, bool limit, bool first, int maxDigit, const string& num) {
if (pos == num.length()) return first ? 0 : 1;
if (dp[pos][limit][first][maxDigit] != -1) return dp[pos][limit][first][maxDigit];
int up = limit ? num[pos] - '0' : 9;
long long ans = 0;
for (int i = 0; i <= up; i++) {
if (first) {
if (i == 0) {
ans += dfs(pos + 1, limit && i == up, true, 0, num);
} else {
ans += dfs(pos + 1, limit && i == up, false, i, num);
}
} else {
if (i < maxDigit) {
ans += dfs(pos + 1, limit && i == up, false, maxDigit, num);
}
}
}
return dp[pos][limit][first][maxDigit] = ans;
}
// 计算小于等于x的蛇形数的数量
long long solve(const string& x) {
memset(dp, -1, sizeof(dp));
return dfs(0, true, true, 0, x);
}
int main() {
string L, R;
cin >> L >> R;
// 将L减1
int i = L.length() - 1;
while (i >= 0 && L[i] == '0') {
L[i] = '9';
i--;
}
L[i]--;
// 计算结果
long long ans = solve(R) - solve(L);
cout << ans << endl;
return 0;
}
D题
链接
#include <iostream>
#include <vector>
#include <queue>
#include <utility>
using namespace std;
const int INF = 1e9;
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
struct State {
int x, y;
bool isVertical;
int steps;
};
int main() {
int H, W;
cin >> H >> W;
vector<string> grid(H);
pair<int, int> start, goal;
for (int i = 0; i < H; ++i) {
cin >> grid[i];
for (int j = 0; j < W; ++j) {
if (grid[i][j] == 'S') start = {i, j};
if (grid[i][j] == 'G') goal = {i, j};
}
}
vector<vector<vector<int>>> dist(H, vector<vector<int>>(W, vector<int>(2, INF)));
queue<State> q;
// 初始状态入队,分别考虑第一次垂直和水平移动
q.push({start.first, start.second, true, 0});
q.push({start.first, start.second, false, 0});
dist[start.first][start.second][0] = dist[start.first][start.second][1] = 0;
while (!q.empty()) {
State cur = q.front();
q.pop();
if (cur.x == goal.first && cur.y == goal.second) {
cout << cur.steps << endl;
return 0;
}
for (int i = 0; i < 4; ++i) {
if ((cur.isVertical && (i < 2)) || (!cur.isVertical && (i >= 2))) continue;
int nx = cur.x + dx[i];
int ny = cur.y + dy[i];
if (nx >= 0 && nx < H && ny >= 0 && ny < W && grid[nx][ny] != '#') {
int newSteps = cur.steps + 1;
bool newIsVertical =!cur.isVertical;
if (newSteps < dist[nx][ny][newIsVertical? 1 : 0]) {
dist[nx][ny][newIsVertical? 1 : 0] = newSteps;
q.push({nx, ny, newIsVertical, newSteps});
}
}
}
}
cout << -1 << endl;
return 0;
}
-
状态表示
- 我们将网格中的每个位置视为一个状态,同时考虑到移动的规则(横竖交替),每个状态还需要记录当前移动是垂直方向还是水平方向。因此,我们定义一个
State
结构体来表示状态,包含位置信息(x, y)
(其中x
表示行,y
表示列)、移动方向信息isVertical
(布尔值,true
表示垂直移动,false
表示水平移动)以及已经走过的步数steps
。 - 为了记录从起点到每个位置在不同移动方向状态下的最短步数,我们使用一个三维数组
dist[H][W][2]
,其中dist[x][y][0]
表示到达位置(x, y)
时最后一步是水平移动的最短步数,dist[x][y][1]
表示到达位置(x, y)
时最后一步是垂直移动的最短步数。初始时,将所有位置的步数设置为无穷大(这里用1e9
表示),起点的两种状态(第一次垂直移动和第一次水平移动)的步数设置为 0。
- 我们将网格中的每个位置视为一个状态,同时考虑到移动的规则(横竖交替),每个状态还需要记录当前移动是垂直方向还是水平方向。因此,我们定义一个
-
广度优先搜索(BFS)的运用
- BFS 是一种用于在图或网格中寻找最短路径的常用算法。在本题中,我们将网格看作一个图,每个单元格是图中的一个节点,相邻的单元格(通过边相连)之间有边连接。
- 初始化一个队列
q
,并将起点的两种初始状态(第一次垂直移动和第一次水平移动)加入队列。 - 进入 BFS 的主循环,只要队列不为空,就取出队首的状态
cur
。 - 检查当前状态
cur
的位置是否为目标位置。如果是,说明已经找到了从起点到终点的路径,此时输出cur.steps
(即当前路径的步数)并结束程序。 - 否则,遍历四个方向(上、下、左、右,分别对应
dx
和dy
数组中的偏移量)。根据当前状态cur
的移动方向isVertical
,如果当前是垂直移动(isVertical
为true
),则跳过水平方向的移动(即i < 2
的情况);反之,如果当前是水平移动(isVertical
为false
),则跳过垂直方向的移动(即i >= 2
的情况)。这样就保证了移动是横竖交替的。 - 计算新的位置
(nx, ny)
,如果新位置在网格范围内(nx >= 0 && nx < H && ny >= 0 && ny < W
)且不是障碍物(grid[nx][ny] != '#'
),则计算新的步数newSteps = cur.steps + 1
,并得到新的移动方向newIsVertical =!cur.isVertical
(因为移动方向要交替)。 - 检查新的步数
newSteps
是否小于当前记录的到达位置(nx, ny)
在新移动方向状态下的最短步数(dist[nx][ny][newIsVertical? 1 : 0]
)。如果是,更新dist
数组,并将新的状态{nx, ny, newIsVertical, newSteps}
加入队列q
。
-
结果判断与输出
- 如果 BFS 循环结束后,队列为空但还没有找到目标位置,说明从起点无法按照规则到达终点,此时输出 -1。