牛客周赛 Round 19 解题报告 | 珂学家

前言


整体评价

这场挺有意思的,尤其是T4, 其实很早之前也想过这个问题?如何智能的扫雷,感觉有点难。这题被逼得主动去求解这个扫雷问题,幸好只有4*4,可以暴力枚举。喜欢这种比赛。


A.  小红的字符串大小写变换

Q: API题,把前k个字符大写,后n-k个字符小写

可以切分为2段,然后分别大写,小写化,然后拼接即可

import java.io.BufferedInputStream;
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();
        int k = sc.nextInt();
        String s = sc.next();
        System.out.println(
                s.substring(0, k).toUpperCase() + s.substring(k).toLowerCase()
        );
    }

}

B. 小红杀怪

Q: 有两种技能,一种单体伤害,一种群体伤害,求消灭2个不同血量的怪物,最多使用多少技能?

这题的数据范围有点小,其实可以枚举群体伤害的次数,然后求单体伤害的次数,从而获得最小值。

切入点就是

枚举全体伤害次数,然后求的单体伤害

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

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int a = sc.nextInt(), b = sc.nextInt();
        int x = sc.nextInt(), y = sc.nextInt();

        int ans = Math.max((a + y - 1) / y, (b + y - 1) / y);
        for (int i = 0; i <= ans; i++) {
            int t1 = (Math.max(a - y * i, 0) + x - 1) / x;
            int t2 = (Math.max(b - y * i, 0) + x - 1) / x;
            ans = Math.min(ans, i + t1 + t2);
        }
        System.out.println(ans);
    }

}

C. 小红的元素分裂

Q: 元素x可以分裂为1, x-1,  或者a, b(x=a*b), 求最小次数

可以预处理,枚举一个数的因子,最多\sqrt{n}, 而因子数均摊为log(n)

可以顺推,其实也可以记忆化搜索

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

public class Main {

    static int SZ = 10_0000;
    static Integer[] memo = new Integer[SZ + 1];

    static int dfs(int u) {
        if (u == 1) return 0;
        if (memo[u] != null) return memo[u];

        int res = u;
        for (int i = 2; i <= u / i; i++) {
            if (u % i == 0) {
                int r1 = dfs(i);
                int r2 = dfs(u / i);
                res = Math.min(res, r1 + r2 + 1);
            }
        }
        int r3 = dfs(u - 1) + 1;
        res = Math.min(res, r3);
        return memo[u] = res;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int n = sc.nextInt();
        long acc = 0;
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
            acc += dfs(arr[i]);
        }
        System.out.println(acc);
    }

}

D. 小红的扫雷游戏

Q: 给一个4x4的扫雷地图,还有一些开的数字格子,求得确定性的雷和非雷,标注‘X’和‘O’?

一开始想着,是否可以搞一个方程组

如果确定为雷和非雷,这些是可解的,还有一些不能明确解的。

然后想想,这我也不会呀,重新审视了下数据范围,4*4,还有雷/非雷, 这个0-1特性,所以想到了全枚举状态,然后进行验证。

如果一个合法的状态,可以对相应的格子进行打标签。

如果一个格子,即被打雷,又被打非雷,那就是不确定,反之就是确定态。

在来分析下时间复杂度为, O(2^{16}* 16 * 8), 这是理论上复杂度,实际上因为游戏本身的限制,并没有这个高。

写出来,还是挺有意思的,这个题目。

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

public class Main {

    static int[][] dirs = new int[][] {
            {-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}
    };

    static boolean verify(char[][] chess, int s) {
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                char c = chess[i][j];
                if (c == '.') continue;
                int p = c - '0';
                int mine = 0;
                for (int t = 0; t < dirs.length; t++) {
                    int y0 = i + dirs[t][0];
                    int x0 = j + dirs[t][1];
                    if (y0 >= 0 && y0 < 4 && x0 >= 0 && x0 < 4) {
                        int ts = y0 * 4 + x0;
                        if ((s & (1 << ts)) != 0) {
                            mine++;
                        }
                    }
                }
                if (mine != p) return false;
            }
        }
        return true;
    }

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

        int notMine = (1 << 16) - 1;
        for (int i = 0; i < 4; i++) {
            chess[i] = sc.next().toCharArray();
            for (int j = 0; j < 4; j++) {
                if (chess[i][j] != '.') {
                    notMine -= 1 << (i * 4 + j);
                }
            }
        }

        // 收藏可能解, 1(0b01)一定是非雷,2(0b10)一定雷,3(0b11)则不确定
        int[][] vis = new int[4][4];

        // 枚举所有的可能性, 因为只有2^16种可能
        int range = 1 << 16;
        for (int i = 0; i < range; i++) {
            if ((i | notMine) == notMine && verify(chess, i)) {
                for (int r = 0; r < 4; r++) {
                    for (int c = 0; c < 4; c++) {
                        if ((i & (1 << (4 * r + c))) == 0) {
                            vis[r][c] |= 1;
                        } else {
                            vis[r][c] |= 2;
                        }
                    }
                }
            }
        }

        // 地雷图还原
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if (chess[i][j] == '.') {
                    if (vis[i][j] == 1) chess[i][j] = 'O';
                    else if (vis[i][j] == 2) chess[i][j] = 'X';
                }
            }
        }

        for (int i = 0; i < 4; i++) {
            System.out.println(new String(chess[i]));
        }
    }

}


写在最后

你可能感兴趣的:(牛客周赛,解题报告,算法)