AtCoderABC387题解

A题

链接

题目大意:给你AB,求$ ( A+B ) ^ 2 $,直接上代码!

#include<iostream>
using namespace std;

signed main() {
	int a,b;
	cin>>a>>b;
	cout<<(a+b)*(a+b);
	return 0;
}

B题

链接

给你一个列表,第$ i $行第$ j $列的值是$ij$,满足$ i ,j \le 9$,让你求这个列表中不等于$n$的数字之和。

因为数很小,根本不用优化,上代码!!!

#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题

链接

  1. 状态表示

    • 我们将网格中的每个位置视为一个状态,同时考虑到移动的规则(横竖交替),每个状态还需要记录当前移动是垂直方向还是水平方向。因此,我们定义一个 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。
  2. 广度优先搜索(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
  3. 结果判断与输出

    • 如果 BFS 循环结束后,队列为空但还没有找到目标位置,说明从起点无法按照规则到达终点,此时输出 -1。
#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;
}    

E题

链接

本题是一道构造题,解题关键在于利用数的整除规则来构造满足条件的 “双子良整数”。

  1. 解题思路
    • 小范围暴力枚举
  2. 代码实现(C++)
#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned;
using u128 = unsigned __int128;

void solve() {
    string s;
    cin >> s;
    int n = s.size();
    if (n >= 5) {
        if (s[0] >= '6') {
            for (int i = 1; i <= n + 1; i++) {
                if (i == 1 || i == 2) {
                    cout << 1;
                } else {
                    cout << 0;
                }
            }
            cout << "\n";
        } else {
            int x = stoi(s.substr(0, 2));
            string ans;
            ans += s[0];
            ans += ('8' - s[0] + '0');
            int y = stoi(ans);
            if (x >= y) {
                ans[0]++;
                ans[1]--;
            }
            cout << ans;
            for (int i = 1; i <= n - 2; i++) {
                cout << 0;
            }
            cout << "\n";
        }
    } else {
        int x = stoi(s);
        for (int i = x; i <= 2 * x - 1; i++) {
            int sum = 0, xx = i, yy = i + 1, sumy = 0;
            while (xx) {
                sum += xx % 10;
                xx /= 10;
            }
            while (yy) {
                sumy += yy % 10;
                yy /= 10;
            }
            if (i % sum == 0 && (i + 1) % sumy == 0) {
                cout << i << "\n";
                return;
            }
        }
        cout << -1 << "\n";
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

除了上述方法,还可以采用启发式搜索策略,如将a的候选数限制为末尾是0且大部分数字为0的整数,通过随机或确定性搜索来寻找满足条件的双子良整数 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值