AtCoder Beginner Contest 308 题解 A-F

A - New Scheme

题目描述

8个整数 S 1 , S 2 , . . . S 8 S_1,S_2,...S_8 S1,S2,...S8,如果这8个数满足下面3个条件,输入Yes,否则输出No

  • S 1 ∼ S 8 S_1 \sim S_8 S1S8是单调非递减的,即 S 1 ≤ S 2 ≤ . . . ≤ S 8 S_1 \le S_2 \le ... \le S_8 S1S2...S8
  • S 1 ∼ S 8 S_1 \sim S_8 S1S8都处于[100,675]
  • S 1 ∼ S 8 S_1 \sim S_8 S1S8都是25的倍数
思路
  • 模拟
public static void solve() throws IOException {
    int cur = 0;
    for (int i = 0; i < 8; i++) {
        int a = readInt();
        if (a < 100 || a > 675 || a % 25 != 0) {
            printWriter.println("No");
            return;
        }
        if (i == 0) {
            cur = a;
        } else {
            if (a < cur) {
                printWriter.println("No");
                return;
            } else {
                cur = a;
            }
        }
    }
    printWriter.println("Yes");
}

B - Default Price

题目描述

高桥在餐厅吃了N个寿司,每个寿司的颜色用一个字符串 C i C_i Ci表示。餐厅的总共有M个寿司,编号为 1 ∼ M 1 \sim M 1M每个寿司 D i D_i Di的颜色有着对应的价格 P i P_i Pi,如果高桥吃的寿司 C i C_i Ci的颜色在餐厅中不存在,则这个寿司的价格是 P 0 P_0 P0求高桥最后吃的寿司总价格

思路
  • 模拟
public static void solve() throws IOException {
    int n = sc.nextInt(), m = sc.nextInt();
    String[] strings = new String[n + 1];
    for (int i = 1; i <= n; i++) {
        strings[i] = sc.next();//吃的寿司
    }
    String[] strings2 = new String[m + 1];
    for (int i = 1; i <= m; i++) {
        strings2[i] = sc.next();//餐厅的寿司
    }
    Map<String, Integer> map = new HashMap<>();
    for (int i = 0; i <= m; i++) {
        int a = sc.nextInt();
        if (i == 0) {
            map.put("TEMP", a);
        } else {
            map.put(strings2[i], a);
        }
    }
    int res = 0;
    for (int i = 1; i <= n; i++) {
        String s = strings[i];
        res += map.containsKey(s) ? map.get(s) : map.get("TEMP");//不存在则价格为P0
    }
    printWriter.println(res);
}

C - Standings

题目描述

N个人,编号为 1 ∼ N 1 \sim N 1N, 他们分别抛硬币,抛到正面的概率为 A i A_i Ai,抛到反面的概率为 B i B_i Bi,定义一个人抛硬币的成功率为 A i A i + B i \frac{A_i} {A_i + B_i} Ai+BiAi 1 , … , N 1,…,N 1,,N人按其成功率从高到低排序,成功率相同则按编号从小到大排序

思路
  • 模拟(注意精度问题)
static class Pair{
    Long a, b;
    int num;
    public Pair (Long a, Long b, int num) {
        this.a = a;
        this.b = b;
        this.num = num;
    }
}

public static void solve() throws IOException {
    int n = readInt();
    List<Pair> list = new ArrayList<>();
    for (int i = 1; i <= n; i++) {
        int a = readInt(), b = readInt();
        list.add(new Pair((long) a, (long) (a + b), i));
    }
    Collections.sort(list, new Comparator<Pair>() {
        @Override
        public int compare(Pair o1, Pair o2) {
            long a = o1.a, b = o1.b, c = o2.a, d = o2.b;
            int num1 = o1.num, num2 = o2.num;
            if (a * d == b * c) {
                return num1 - num2;
            }
            // 为什么是 b*c在前?假设a=1,b=2,c=3,d=4,那么b*c > a*d,即可实现 a/(a+b)升序排序
            return b * c - a * d > 0 ? 1 : -1;
        }
    });
    for (Pair pair : list) {
        printWriter.print(pair.num + " ");
    }
}

解决精度问题:有时会出现一个整数除以一个整数得到浮点数 d d d,但是这个 d d d又需要被存储从而用来排序,但是由于精度丢失问题,导致实际的排序与实际结果不同。例如比较 a b \frac {a} {b} ba c d \frac {c} {d} dc的大小,如果通过double来比较,可能会出现精度问题导致结果错误,解决方法是将左右两边都乘以 b d bd bd相当于比较 a ∗ d a*d ad b ∗ c b*c bc的大小,但是要注意数据范围,可能会溢出,开long就好了。

D - Snuke Maze

题目描述

有一个HW列的网格,史努克位于左上角,问是否存在一条这样的路径: s → n → u → k → e → s → n → . . . s \to n \to u \to k \to e \to s \to n \to ... snukesn...,史努克可以朝着上下左右四个方向移动,从而让史努克到达右下角,如果存在,输出Yes,否则输出No

思路
  • bfs
static int n, m;
static char[][] map;
static boolean[][] st;
static int[] dx = new int[] {-1, 0, 1, 0}, dy = new int[] {0, 1, 0, -1};

public static void solve() throws IOException {
    n = sc.nextInt(); m = sc.nextInt();
    map = new char[n + 1][m + 1];
    st = new boolean[n + 1][m + 1];
    for (int i = 1; i <= n; i++) {
        map[i] = (" " + sc.next()).toCharArray();//空格占位,从第一列开始枚举
    }
    printWriter.println(bfs() ? "Yes" : "No");
}

public static boolean bfs() {
    if (map[1][1] != 's') return false;
    Deque<int[]> deque = new LinkedList<>();
    deque.offer(new int[] {1, 1});
    st[1][1] = true;
    while (deque.size() > 0) {
        int[] t = deque.poll();
        if (t[0] == n && t[1] == m) return true;//可以到达
        char ch = map[t[0]][t[1]];
        boolean f = false;
        for (int i = 0; i < 4; i++) {
            int nx = t[0] + dx[i], ny = t[1] + dy[i];
            if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && !st[nx][ny]) {
            	//枚举五种情况
                if (ch == 's' && map[nx][ny] == 'n') {
                    deque.offer(new int[] {nx, ny});
                    st[nx][ny] = true;
                } else if (ch == 'n' && map[nx][ny] == 'u') {
                    deque.offer(new int[] {nx, ny});
                    st[nx][ny] = true;
                } else if (ch == 'u' && map[nx][ny] == 'k') {
                    deque.offer(new int[] {nx, ny});
                    st[nx][ny] = true;
                } else if (ch == 'k' && map[nx][ny] == 'e') {
                    deque.offer(new int[] {nx, ny});
                    st[nx][ny] = true;
                } else if (ch == 'e' && map[nx][ny] == 's') {
                    deque.offer(new int[] {nx, ny});
                    st[nx][ny] = true;
                }
            }
        }
    }
    return false;
}

E - MEX

题目描述

有个长度为N的序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,,AN),由 0 0 0 1 1 1 2 2 2 组成,以及一个长度为N的字符数组 S = S 1 S 2 … S N S=S_1S_2…S_N S=S1S2SN ,由 MEX 组成。对于所有符合条件 1 ≤ i < j < k ≤ N 1 \le i \lt j \lt k \le N 1i<j<kN S i S j S k S_iS_jS_k SiSjSk=MEX的三元组 ( i , j , k ) (i,j,k) (i,j,k)请你求出 m e x ( A i , A j , A k ) mex(A_i,A_j,A_k) mex(Ai,Aj,Ak)之和,其中 m e x mex mex函数表示未出现在序列中的最小非负整数。

样例输入
4
1 1 0 2
MEEX
样例输出
3
样例解释:

使得 S i S j S k S_iS_jS_k SiSjSk= MEX的元组有以下两个: ( i , j , k ) = ( 1 , 2 , 4 ) , ( 1 , 3 , 4 ) (i,j,k)=(1,2,4),(1,3,4) (i,j,k)=(1,2,4),(1,3,4).因为 mex ( A 1 , A 2 , A 4 ) = mex ( 1 , 1 , 2 ) = 0 \text{mex}(A_1,A_2,A_4)=\text{mex}(1,1,2)=0 mex(A1,A2,A4)=mex(1,1,2)=0(因为最小未出现的数字是0)和 mex ( A 1 , A 3 , A 4 ) = mex ( 1 , 0 , 2 ) = 3 \text{mex}(A_1,A_3,A_4)=\text{mex}(1,0,2)=3 mex(A1,A3,A4)=mex(1,0,2)=3(因为最小未出现的数字是3),所以答案是 0 + 3 = 3 0+3=3 0+3=3

思路
  • 先求出mex函数,即求出3个数中为出现的最小的数
  • 遍历字符数组S中的所有E,分别求出当前E的前面的M和后面的X的个数
public static void solve() throws IOException {
    int n = sc.nextInt();
    int[] nums = new int[n + 1];
    char[] chars = new char[n + 1];
    for (int i = 1; i <= n; i++) {
        nums[i] = sc.nextInt();
    }
    chars = (" " + sc.next()).toCharArray();//空格占位
    //ms统计当前位置的E的前面有多少个M
    //xs统计当前位置的E的后面有多少个X
    int[][] ms = new int[n + 1][3], xs = new int[n + 1][3];
    int m0 = 0, m1 = 0, m2 = 0, x0 = 0, x1 = 0, x2 = 0;
    //统计当前 e的前面有多少个 m和它后面有多少个 x
    for (int i = 1, j = n; i <= n; i++, j--) {
        if (chars[i] == 'E') {
            ms[i][0] = m0; ms[i][1] = m1; ms[i][2] = m2;
        } else if (chars[i] == 'M') {
            if (nums[i] == 0) m0++;
            else if (nums[i] == 1) m1++;
            else if (nums[i] == 2) m2++;
        }
        if (chars[j] == 'E') {
            xs[j][0] = x0;
            xs[j][1] = x1;
            xs[j][2] = x2;
        } else if (chars[j] == 'X') {
            if (nums[j] == 0) x0++;
            else if (nums[j] == 1) x1++;
            else if (nums[j] == 2) x2++;
        }
    }
    long sum = 0;
    for (int i = 2; i <= n - 1; i++) {//从 2枚举到 n-1
        if (chars[i] == 'E') {
            for (int j = 0; j < 3; j++) {
                if (ms[i][j] == 0) continue;//当前E前面没有mj
                for (int k = 0; k < 3; k++) {
                    if (xs[i][k] == 0) continue;//当前E后面没有xk
                    sum += (long) mex(nums[i], j, k) * ms[i][j] * xs[i][k];//统计,乘法原理
                }
            }
        }
    }
    printWriter.println(sum);
}

//求出三个数中最小的且没有出现过的数
public static int mex(int x, int y, int z) {
    for (int i = 0; i <= 2; i++) {
        if (x != i && y != i && z != i) return i;
    }
    return 3;
}

考点:前后缀分解

F - Vouchers

题目描述

你想在商店里买N件商品,第i件商品的价格为 P i P_i Pi,现在你有M张优惠券,第i张优惠券需要满 L i L_i Li D i D_i Di,一件物品最多只能使用一张优惠券,每一张优惠券最多使用一次。现在请你求出购买所有N件商品所需的最小总金额

思路
  • 先按照优惠券的优惠力度 D i D_i Di降序排序,这样可以尽可能地减少花费
  • 再按照优惠券需要满足的 L i L_i Li降序排序,这样可以尽可能地多用优惠券,间接减少花费
  • TreeMapceilingKey()方法获取最小的可以满足 L i L_i Li价格的商品
public static void solve() throws IOException {
    int n = readInt(), m = readInt();
    long sum = 0;
    TreeMap<Integer, Integer> map = new TreeMap<>();
    for (int i = 1; i <= n; i++) {
        int a = readInt();
        map.put(a, map.getOrDefault(a, 0) + 1);
        sum += a;//先统计所有商品的价格之和,即将所有商品购买
    }
    //优先队列先按照Di降序排序,再按照Li降序排序
    PriorityQueue<int[]> deque = new PriorityQueue<>(new Comparator<int[]>() {
        @Override
        public int compare(int[] o1, int[] o2) {
            if (o1[1] == o2[1]) {
                return o2[0] - o1[0];
            }
            return o2[1] - o1[1];
        }
    });
    int[] l = new int[m + 1], d = new int[m + 1];
    for (int i = 1; i <= m; i++) {
        l[i] = readInt();
    }
    for (int i = 1; i <= m; i++) {
        d[i] = readInt();
    }
    for (int i = 1; i <= m; i++) {
        deque.offer(new int[] {l[i], d[i]});
    }
    //不断枚举优惠券,做到能用则用
    while (deque.size() > 0) {
        int[] t = deque.poll();
        Integer key = map.ceilingKey(t[0]);//获取大于等于t[0]的最小的键,O(logn)
        if (key != null) {
            sum -= t[1];//可以使用,则花费总额减少Di
            map.put(key, map.getOrDefault(key, 0) - 1);
            if (map.get(key) == 0) map.remove(key);//优惠券用完就移除
        }
    }
    printWriter.println(sum);
}

考点:1. 优先队列的使用和排序方式 2. TreeMapceilingKey()方法的使用

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
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$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值