AtCoder Beginner Contest 308 A-F题解

文章介绍了ACM比赛中的三道题目,包括模拟题目的解法(A题),利用map和模拟策略解决颜色价值问题(B题),涉及结构体排序和精度计算(C题),深度优先搜索迷宫问题(D题),以及动态规划和贪心策略在优惠券应用中的优化(F题)。
摘要由CSDN通过智能技术生成

比赛链接https://atcoder.jp/contests/abc308

比赛时间:2023年7月1日 20:00-21:40

A题:New Scheme

标签:模拟

题意:给定 8 8 8个数的序列,询问这些数是否满足以下条件:

  1. 100 100 100 675 675 675之间且能被 25 25 25整除
  2. 序列是单调非递减的

题解:按题意模拟判断就好了。

代码

#include <bits/stdc++.h>
using namespace std;
 
int s[10];
 
int main() {
    for (int i = 1; i <= 8; i++) {
        cin >> s[i];
        if (s[i] < 100 || s[i] > 675 || s[i] % 25 != 0) {
            cout << "No";
            return 0;
        }
    }
    for (int i = 2; i <= 8; i++) {
        if (s[i] < s[i - 1]) {
            cout << "No";
            return 0;
        }
    }
    cout << "Yes";
    return 0;
}

B题:Default Price

标签 S T L 、 m a p STL、map STLmap、模拟

题意:给定 n n n个有颜色的盘子和 m m m种颜色分别对应的价值 p i p_i pi p 0 p_0 p0表示盘子颜色如果不属于这 m m m种颜色,那对应的价值,求这 n n n个盘子最后的价值和为多少。

题解:可以先用 m a p map map去映射一下不同颜色对应的价值,然后去遍历这 n n n个盘子,如果映射发现对应的是 0 0 0价值,说明不属于这 m m m种颜色,那我们用 p 0 p_0 p0去代替一下,最后累加下就好了。

代码

#include <bits/stdc++.h>
using namespace std;
 
map<string, int > a;
string s[105], s2[105];
 
int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> s[i];
    }
    for (int i = 1; i <= m; i++) {
        cin >> s2[i];
    }
    int p, v;
    cin >> p;
    for (int i = 1; i <= m; i++) {
        cin >> v;
        a[s2[i]] = v;
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (!a[s[i]]) {
            ans += p;
        } else {
            ans += a[s[i]];
        }
    }
    cout << ans << endl;
    return 0;
}

C题:Standings

标签:结构体排序、精度

题意:给定 n n n人扔硬币,第 i i i个人扔硬币,有正面朝上 A i A_i Ai次和反面朝上 B i B_i Bi次。定义成功率为 A i A i + B i A_i\over A_i+B_i Ai+BiAi。求按成功率从高到低,成功率相同根据编号从小到大排序。

题解:先把每个人的编号 i d id id按输入顺序设置好,然后根据题目中成功率和编号的要求排序就好了。这道题有个坑点就是,直接用 d o u b l e double double除出来会有精度缺失,所以我们把除法运算转成乘法运算。如果有两个人 x x x y y y,那我们把不等式的分母乘到对面去得到 ( x . a + x . b ) ∗ y . a < ( y . a + y . b ) ∗ x . a (x.a + x.b) * y.a < (y.a + y.b) * x.a (x.a+x.b)y.a<(y.a+y.b)x.a

代码

#include <bits/stdc++.h>
using namespace std;
 
struct node {
    int id;
    long long a, b;
}p[2000005];
 
bool cmp(node x, node y) {
    if ((x.a + x.b) * y.a == (y.a + y.b) * x.a) return x.id < y.id;
    else return (x.a + x.b) * y.a < (y.a + y.b) * x.a;
}
 
int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> p[i].a >> p[i].b;
        p[i].id = i;
    }
    sort(p + 1, p + 1 + n, cmp);
    for (int i = 1; i <= n; i++) {
        cout << p[i].id << " ";
    }
    return 0;
}

D题:Snuke Maze

标签:深度优先搜索

题意:给定一个二维的字符矩阵地图,询问能否从左上角 ( 1 , 1 ) (1,1) (1,1)位置走到右下角 ( n , m ) (n,m) (n,m)位置,要求按照字符串(snuke): s → n → u → k → e → s → n →…这样的顺序走。

题解:经典的 d f s dfs dfs问题,按照题目要求从起点深搜下去就好了。深搜问题主要从当前状态往下一个状态走的时候需要考虑几个问题:

  1. 题目限制(该题就是 snuke 的顺序)。
  2. 地图边界。
  3. 该点是否已经走过(避免套娃)。

代码

#include <bits/stdc++.h>
using namespace std;
 
string s[505];
int dx[4] = {0, 0, -1, 1};
int dy[4] = {-1, 1, 0, 0};
int n, m, f = 0;
bool vis[505][505];
map<char, int> g;
 
void dfs(int x, int y, int id) {
    if (f || (x == n - 1 && y == m - 1)) {
        f = 1;
        return ;
    }
 
    for (int i = 0; i < 4; i++) {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
        if (vis[nx][ny]) continue;
 
        if ((g[s[nx][ny]] == id + 1) || (id == 5 && g[s[nx][ny]] == 1)) {
            vis[nx][ny] = 1;
            dfs(nx, ny, g[s[nx][ny]]);
        }
 
    }
}
 
int main() {
    g['s'] = 1; g['n'] = 2; g['u'] = 3;
    g['k'] = 4; g['e'] = 5;
    cin >> n >> m;
    for (int i = 0; i < n; i++) cin >> s[i];
    vis[0][0] = 1;
    dfs(0, 0, g[s[0][0]]);
    if (f) cout << "Yes";
    else cout << "No";
    return 0;
}

E题:MEX

标签:动态规划、前缀维护、MEX

题意:给定一个长度为 n n n的序列 A 1 , A 2 , A 3 , . . . A n A_1,A_2,A_3,...A_n A1,A2,A3,...An A i A_i Ai的范围在 0 、 1 、 2 0、1、2 012。再给定相同长度的字符串 B B B(只含 M 、 E 、 X M、E、X MEX三种字符)。给定规则 M E X MEX MEX:指除去本身以外其他非负整数的集合中的最小值。目前要求 ( B i , B j , B k ) (B_i,B_j,B_k) (Bi,Bj,Bk)刚好是 M 、 E 、 X M、E、X MEX,求所有对应的 M E X ( A i , A j , A k ) MEX(A_i,A_j,A_k) MEX(Ai,Aj,Ak)之和为多少。

题解:维护一个前缀 M M M出现 0 、 1 、 2 0、1、2 012的数量,维护一个后缀 X X X出现 0 、 1 、 2 0、1、2 012的数量,然后去枚举 E E E,到每一个 E E E的时候,看看对应当前的 A i A_i Ai为多少,然后考虑在这之前对应的 M M M 0 、 1 、 2 0、1、2 012的数量,和在这之后 X X X 0 、 1 、 2 0、1、2 012的数量。然后枚举这 9 9 9种情况,每种情况贡献的值为:前缀 M M M的数量 ∗ * 后缀 X X X的数量 ∗ * M E X ( a [ i ] , j , k ) MEX(a[i], j, k) MEX(a[i],j,k) j , k j,k j,k为枚举 0 、 1 、 2 0、1、2 012的情况。

代码

#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const ll N = 2e5 + 10;
ll n, a[N], m[N][10], x[N][10], ans = 0;
char b[N];
 
int cal(int x, int y, int z) {
    for (int i = 0; i <= 3; i++) {
        if (x != i && y != i && z != i) return i;
    }
    return 0;
}
 
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> b[i];
    for (int i = 1; i <= n; i++) {
        if (b[i] == 'M') {
            m[i][a[i]]++;
        }
        m[i][0] += m[i-1][0];
        m[i][1] += m[i-1][1];
        m[i][2] += m[i-1][2];
    }
    for (int i = n; i >= 1; i--) {
        if (b[i] == 'X') {
            x[i][a[i]]++;
        }
        x[i][0] += x[i+1][0];
        x[i][1] += x[i+1][1];
        x[i][2] += x[i+1][2];
    }
 
    for (int i = 1; i <= n; i++) {
        if (b[i] == 'E') {
            if (a[i] == 0) {
                for (int j = 0; j <= 2; j++) {
                    for (int k = 0; k <= 2; k++) {
                        ans += m[i][j] * x[i][k] * cal(0, j, k);
                    }
                }
            }
            else if (a[i] == 1) {
                for (int j = 0; j <= 2; j++) {
                    for (int k = 0; k <= 2; k++) {
                        ans += m[i][j] * x[i][k] * cal(1, j, k);
                    }
                }
            } else {
                for (int j = 0; j <= 2; j++) {
                    for (int k = 0; k <= 2; k++) {
                        ans += m[i][j] * x[i][k] * cal(2, j, k);
                    }
                }
            }
        }
    }
    cout << ans << endl;
    return 0;
}

F题:Vouchers

标签:贪心、堆优化

题意:给定 n n n个商品,每个商品价格为 p i p_i pi元,有 m m m张优惠券,第 i i i优惠券至少要购买价格为 L i L_i Li元的商品才能折扣 D i D_i Di元,每张优惠券只能用一次,同一物品不能使用多张优惠券。求购买所有商品的最少总金额为多少。

题解:按照出售价格从小往大,因为有一个条件 L > D L>D L>D,所以不可能出现折扣大于商品的情况,用一个堆保存小于该商品售价所有 L L L对应的折扣 D D D,每次贪心取折扣最大的,取完就弹出堆,保证只取一次。

代码

#include <bits/stdc++.h>
using namespace std;
 
typedef long long ll;
ll n, m, p[200005], ans;
priority_queue<int> q;
pair<int, int> c[200005];
 
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> p[i];
        ans += p[i];
    }
    for (int i = 1; i <= m; i++) cin >> c[i].first;
    for (int i = 1; i <= m; i++) cin >> c[i].second;
    sort(p + 1, p + 1 + n);
    sort(c + 1, c + 1 + m);
    for (int i = 1, pos = 1; i <= n; i++) {
        while (pos <= m && c[pos].first <= p[i]) {
            q.push(c[pos].second);
            pos++;
        }
        if (!q.empty()) {
            ans -= q.top(); q.pop();
        }
    }
    cout << ans << endl;
    return 0;
}
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值