牛客周赛 Round 1 解题报告 | 珂学家 | 分类计数 + 同余DP

游游画U

https://ac.nowcoder.com/acm/contest/60245/A


前言

alt

生于生时,亡于亡刻。遵从自心,尽人之事。



整体评价

终于等来了侧重面试的比赛,而且题量刚刚好,不超纲,不涉及算法竞赛。

第一场的比赛,感觉题目出的比较典,A是简单模拟,B则是计数题,C则是贪心思路,D是经典的同余DP。

唯一吐槽的是,牛客好像当前只JDK 11, 用不了var.


A. 游游画U

找规律题吧,就是找到底托,中间对等分开,然后往两边靠齐。

而N,则限定了最终图的长宽高,还有就是‘*’的长度

import java.io.BufferedInputStream;
import java.util.Arrays;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int n = sc.nextInt();
        
        // 定义结果的画布
        char[][] res = new char[4 * n][4 * n];
        
        // 前面的3*n行
        for (int i = 0; i < 3 * n; i++) {
            Arrays.fill(res[i], '.');
            for (int j = 0; j < n; j++) {
                res[i][j] = res[i][4*n - 1 - j] = '*';
            }
        }
        
        // 最下面的n行
        for (int i = 3 * n; i < 4 * n; i++) {
            int offset = i + 1 - 3 * n;
            Arrays.fill(res[i], '.');
            for (int j = 0; j < n; j++) {
                res[i][offset + j] = res[i][4 * n - 1 - offset - j] = '*';
            }
        }
        
        // 输出内容
        for (int i = 0; i < 4 * n; i++) {
            System.out.println(new String(res[i]));
        }


    }

}

B. 游游的数组染色

先按同值进行分类,然后内部按颜色进行计数

那最终的结果为

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

    public static void main(String[] args) {
        AReader sc = new AReader();
        int n = sc.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }
        char[] str = sc.next().toCharArray();

        Map<Integer, TreeMap<Character, Integer>> range = new HashMap<>();
        for (int i = 0; i < n; i++) {
            if (!range.containsKey(arr[i])) {
                range.put(arr[i], new TreeMap<>());
            }
            range.get(arr[i]).merge(str[i], 1, Integer::sum);
        }

        long ans = 0;
        for (Map.Entry<Integer, TreeMap<Character, Integer>> kv: range.entrySet()) {
            TreeMap<Character, Integer> ts = kv.getValue();
            long acc = 0;
            for (Map.Entry<Character, Integer> kv2: ts.entrySet()) {
                acc += kv2.getValue();
            }

            for (Map.Entry<Character, Integer> kv2: ts.entrySet()) {
                ans += (long)(acc - kv2.getValue()) * kv2.getValue();
            }
        }

        System.out.println(ans / 2);
    }

    static
    class AReader {
        private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        private StringTokenizer tokenizer = new StringTokenizer("");
        private String innerNextLine() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                return null;
            }
        }
        public boolean hasNext() {
            while (!tokenizer.hasMoreTokens()) {
                String nextLine = innerNextLine();
                if (nextLine == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(nextLine);
            }
            return true;
        }
        public String nextLine() {
            tokenizer = new StringTokenizer("");
            return innerNextLine();
        }
        public String next() {
            hasNext();
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

//        public BigInteger nextBigInt() {
//            return new BigInteger(next());
//        }
        // 若需要nextDouble等方法,请自行调用Double.parseDouble包装
    }

}


C. 游游的交换字符

分类讨论,即构造1开头的串,和0开头的串,两者那个代价小。

一旦确定分头,实际上代价就定下来,重要的是如何计算。

比如当前串,以及构建好了'01...1', 你要寻找下一个‘0’, 则需要多少代价呢?

这个时候,就是从原串中,下一个‘0’之前有几个'1'呢

同理,‘1’也是统计前置几个‘0’

那这个动态有变动的统计0-1该怎么处理呢?

我们可以尝试引入 0-1 树状数组

就是有一个单独统计‘1’的位置的树状数组,一个统计‘0’的树状数组

前置的01构建完后,就删掉对应的01位置即可。

import java.io.BufferedInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {

    static class BIT {
        int n;
        int[] arr;
        public BIT(int n) {
            this.n = n;
            this.arr = new int[n + 1];
        }
        void update(int p, int d) {
            while (p <= n) {
                arr[p] += d;
                p += p & -p;
            }
        }
        int query(int p) {
            int res = 0;
            while (p > 0) {
                res += arr[p];
                p -= p & -p;
            }
            return res;
        }
    }

    // *)
    public static long solve(char[] buf, char s) {
        // 找到最近的0
        int n = buf.length;
        BIT bit0 = new BIT(n);
        BIT bit1 = new BIT(n);

        List<Integer> list0 = new ArrayList<>();
        List<Integer> list1 = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            if (buf[i] == '0') {
                list0.add(i + 1);
                bit0.update(i + 1, 1);
            } else {
                list1.add(i + 1);
                bit1.update(i + 1, 1);
            }
        }

        long ans = 0l;

        if (s == '0') {
            if (list0.size() < list1.size()) return Long.MAX_VALUE;

            for (int i = 0; i < n; i++) {
                if (i % 2 == 0) {
                    int idx = list0.get(i/2);
                    int delta = bit1.query(idx);
                    bit0.update(idx, -1);

                    ans += delta;
                } else {
                    int idx = list1.get(i/2);
                    int delta = bit0.query(idx);
                    bit1.update(idx, -1);
                    ans += delta;
                }
            }

        } else {

            if (list1.size() < list0.size()) return Long.MAX_VALUE;

            for (int i = 0; i < n; i++) {
                if (i % 2 == 0) {
                    int idx = list1.get(i/2);
                    int delta = bit0.query(idx);
                    bit1.update(idx, -1);

                    ans += delta;
                } else {
                    int idx = list0.get(i/2);
                    int delta = bit1.query(idx);
                    bit0.update(idx, -1);
                    ans += delta;
                }
            }
        }

        return ans;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        char[] buf = sc.next().toCharArray();

        long res = Math.min(solve(buf.clone(), '0'), solve(buf.clone(), '1'));

        System.out.println(res);
    }

}

不过这个解法,应该是写复杂了。

直接贪心即可

就是

import java.io.BufferedInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {

    // *)
    public static long solve(char[] buf, char s) {
        // 找到最近的0
        int n = buf.length;
        
        List<Integer> list0 = new ArrayList<>();
        List<Integer> list1 = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            if (buf[i] == '0') {
                list0.add(i);
            } else {
                list1.add(i);
            }
        }

        long ans = 0l;

        if (s == '0') {
            if (list0.size() < list1.size()) return Long.MAX_VALUE;
            for (int i = 0; i < list0.size(); i++) {
                ans += Math.abs(2 * i - list0.get(i)); 
            }
        } else {
            if (list1.size() < list0.size()) return Long.MAX_VALUE;
            for (int i = 0; i < list1.size(); i++) {
                ans += Math.abs(2 * i - list1.get(i)); 
            }
        }

        return ans;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        char[] buf = sc.next().toCharArray();

        long res = Math.min(solve(buf.clone(), '0'), solve(buf.clone(), '1'));

        System.out.println(res);
    }

}

D. 游游的9的倍数

经典的同余DP

因为求的是9的倍数,而数字串前缀

所以我们令

状态转移为

当然这边对空集合的处理,有个小技巧,就是单独加1

可以借助滚动数组来优化DP的空间

复杂度分析,时间, 空间复杂度为

import java.io.BufferedInputStream;
import java.util.Arrays;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        char[] buf = sc.next().toCharArray();

        long mod = 10_0000_0007l;
        long[][] opt = new long[2][9];
        opt[0][0] = 0l;

        int prev = 0, next = 1;
        for (int i = 0; i < buf.length; i++) {
            int p = buf[i] - '0';

            // 添加/不添加
            Arrays.fill(opt[next], 0);

            for (int j = 0; j < 9; j++) {
                opt[next][j] = opt[prev][j];
            }
            for (int j = 0; j < 9; j++) {
                opt[next][(j * 10 + p) % 9] += opt[prev][j];
                opt[next][(j * 10 + p) % 9] %= mod;
            }

            opt[next][p % 9] = (opt[next][p % 9] + 1) % mod;

            prev = 1 - prev;
            next = 1 - next;
        }

        System.out.println(((opt[prev][0]) % mod + mod) % mod);

    }


}

如果9是个变量k,那其实也是同一种套路。

如果此题不用DP,能否解呢? 感觉有,因为9做为除数比较特殊,它打破了字符串的顺序限制。


写在最后

没有绝对的善意,关切的目光中也会掺杂恶意的期待。

alt

牛客周赛解题报告系列 文章被收录于专栏

牛客周赛,定位求职笔试真题,倾向于面试算法

全部评论
请问第三题比如说字符1比较多, 我先算出1所有应该在的下标之和, 再减去实际在的下标之和, 为什么不可以呢
点赞 回复 分享
发布于 2023-07-12 13:35

相关推荐

10-21 23:48
蚌埠坦克学院
csgq:可能没hc了 昨天一面完秒挂
点赞 评论 收藏
分享
头像
10-22 19:18
上海大学 后端
jopajhhdjwnqk:水印都叠杀人书了
点赞 评论 收藏
分享
7 1 评论
分享
牛客网
牛客企业服务