微软2017年预科生计划在线编程笔试,附解题思路

微软2017年预科生计划在线编程第一场笔试题目题解

GitHub两场考试的代码

#1489 : Legendary Items

这里写图片描述
首先需要看懂题目,博主一开始就是题目没看懂。导致一直WA。
大概思路就是计算每次得到一个传奇(legendary)需要几次探索,不要被树的结构迷惑了。这里附上一个hihocoder的github第一题讲解。以下图摘自上面的链接里。
这里写图片描述
通过观察可以发现红圈中的子树和黄圈中的子树一模一样。这提示我们,无论第一件传说物品是怎么获得的(完成第一件任务就获得还是完成两件任务后获得),第二件传说物品都是从25%概率开始,与第一件无关。因为每得到一个传奇后,概率是变成P/(2^i),这里i是当前传奇的数量,也就是说,如果这次以P的概率得到传奇了,那么下次得到传奇的概率会是P/2,如果下次不能得到传奇(1-P/2),那么下下次得到传奇的概率是P/2+Q,而不是(1-P/2)+Q。
如下图:
这里写图片描述
换句话说,每件传说物品的获得都是独立的,就好像掷N枚骰子每枚骰子的点数是相互独立的一样。我们知道如果X和Y是两个独立的随机变量,那么E(X+Y)=E(X)+E(Y)。于是我们可以分别求出获得第一件、第二件……第N件传说物品的期望任务数,再把它们加起来就是最终答案。
Accept代码

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int P = sc.nextInt();
        int Q = sc.nextInt();
        int N = sc.nextInt();
        double result = 0.0;
        double double_Q = 1.0 * Q / 100;
        for (int i = 0; i < N; i++) {
            double getLegendary = getNextLegendary(P, double_Q);
            result += getLegendary;
            P /= 2;//得到一个传奇后,概率变为 ⌊P/(2^i)⌋%  i代表得到的传奇数
        }
        System.out.printf("%.2f\n", result);
    }

    static double getNextLegendary(int P, double q) {
        double double_p = 1.0 * P / 100;
        double needQuest = 1;//得到下一个传奇,必须至少需要的quest
        double another = 1;//额外需要的quest
        while (true) {
            another *= (1 - double_p);//每次探索,额外需要的quest概率增加
            needQuest += another;
            double_p += q;//每次都在p的基础上加q
            if (double_p >= 1.0)
                break;
        }
        return needQuest;
    }
}

另外还有一种更优化的方法,可以参考上面的链接的方法。时间复杂度更低,后续有时间再加上吧!以上思路和我再微软笔试第一场里的讨论代码是一样的。

#1490 : Tree Restoration
这里写图片描述

首先思路就是从叶子节点开始计算,对于距离矩阵,需要扩展到N*N而不是K*K。 需要计算每个节点之间的距离。如果同行相邻的节点之间的距离大于2,说明它们不是同一个父节点。那么上一层对应的父节点的坐标需要后移,在得到当前节点的父节点后,需要更新父节点到其他节点之间的距离,因为父节点后续会成为子节点,我们需要它和它相邻节点的距离。如果上层节点是叶子节点,就跳过。这道题就是复杂了一点,需要保存节点,距离矩阵,剩下的就是过程繁琐一点。

import java.util.List;
import java.util.Scanner;

/**
 * Created by ZhengChaoJie on 2017/4/1/0001.
 */
public class Main {
    public static void main(String[] args) {
        int n, m, k;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        m = scanner.nextInt();
        k = scanner.nextInt();
        int[][] nodes = new int[m][];
        for (int i = 0; i < m; i++) {
            int count = scanner.nextInt();
            nodes[i] = new int[count];
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < nodes[i].length; j++) {
                nodes[i][j] = scanner.nextInt();
            }
        }
        int[] leaf = new int[k];
        for (int i = 0; i < k; i++)
            leaf[i] = scanner.nextInt();

        int[][] distance = new int[n + 1][n + 1];
        for (int i = 0; i <= n; i++)
            for (int j = 0; j <= n; j++)
                distance[i][j] = -1;

        for (int i = 0; i < k; i++) {
            for (int j = 0; j < k; j++) {
                distance[leaf[i]][leaf[j]] = scanner.nextInt();
            }
        }
        int[] parentList = findParent(nodes, distance, leaf, n, m, k);

        for (int i = 1; i < n; i++)
            System.out.print(parentList[i] + " ");
        System.out.println(parentList[n]);
    }

    static int[] findParent(int[][] nodes, int[][] distance, int[] leaf, int n, int m, int k) {
        int[] parentList = new int[n + 1];
        boolean[] isLeaf = new boolean[n + 1];
        for (int i = 0; i < leaf.length; i++) {
            isLeaf[leaf[i]] = true;
        }
        int curLevel = nodes.length - 1, index = 0, parentIndex = 0;
        while (curLevel > 0) {
            while (parentIndex < nodes[curLevel - 1].length && isLeaf[nodes[curLevel - 1][parentIndex]])
                parentIndex++;
            while (index < nodes[curLevel].length) {
                parentList[nodes[curLevel][index]] = nodes[curLevel - 1][parentIndex];
                for (int i = 0; i < distance.length; i++) {
                    if (i != nodes[curLevel - 1][parentIndex]) {
                        distance[i][nodes[curLevel - 1][parentIndex]] = distance[i][nodes[curLevel][index]] - 1;
                        distance[nodes[curLevel - 1][parentIndex]][i] = distance[nodes[curLevel][index]][i] - 1;
                    } else
                        distance[i][i] = 0;
                }
                if (index + 1 < nodes[curLevel].length && distance[nodes[curLevel][index]][nodes[curLevel][index + 1]] > 2) {
                    parentIndex++;
                    while (parentIndex < nodes[curLevel - 1].length && isLeaf[nodes[curLevel - 1][parentIndex]])
                        parentIndex++;
                }
                index++;
            }
            curLevel--;
            parentIndex = 0;
            index = 0;
        }
        return parentList;
    }
}

#1491 : Monster Killing
这里写图片描述
终于把微软第三题做出来了,看了一个dp教程,感觉还是受益良多。查看下面的解题思路。总的状态有2^20*6种。其中2^20是矩阵最多有的状态,6是有buffer0~5六种状态。这里用到了类似位压缩的方法,对于矩阵(最大有20格子),每个格子有两种选择:揭开了还是没有揭开。所以我们可以将揭开的格子设置为‘D’,然后计算目前格子对应的状态值是多少,具体看代码。这里dp[i][j]代表的是,剩余i个buffer,在j状态(计算出来的int值),最少需要多少hp才能杀死所有monster。最后结果就是HP-dp[5][initstate]。
附上代码:

package online1.third;

import java.util.*;

class Monster {
    String ij;
    int hp;
    int ap;

    Monster(String ij, int hp, int ap) {
        this.ij = ij;
        this.hp = hp;
        this.ap = ap;
    }
}

public class Main {
    private static final int MAX_STATE = (1 << 20), ROUND = 5;
    static int N, M, HP, AP;
    static int[][] dp = new int[ROUND + 1][MAX_STATE + 1];
    static Map<String, Monster> monsterMap = new HashMap<>();

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        M = scanner.nextInt();
        char[][] mazeMap = new char[N][M];
        List<String> monsterIndex = new ArrayList<>();
        scanner.nextLine();
        for (int i = 0; i < N; i++) {
            String line = scanner.nextLine();
            line = line.trim();
            mazeMap[i] = line.toCharArray();
            for (int j = 0; j < M; j++) {
                if (mazeMap[i][j] == 'S' || mazeMap[i][j] == 'M')
                    monsterIndex.add(i + "" + j);
            }
        }
        for (int pos = 0; pos < monsterIndex.size(); pos++) {
            int hp, ap;
            hp = scanner.nextInt();
            ap = scanner.nextInt();
            String index = monsterIndex.get(pos);
            Monster monster = new Monster(index, hp, ap);
            monsterMap.put(index, monster);
        }
        HP = scanner.nextInt();
        AP = scanner.nextInt();
        for (int i = 0; i <= ROUND; i++) {
            for (int j = 0; j <= MAX_STATE; j++) {
                dp[i][j] = Integer.MAX_VALUE;
            }
        }
        int remainHp = HP - letKill(mazeMap, ROUND);
        if (remainHp <= 0)
            System.out.println("DEAD");
        else
            System.out.println(remainHp);
    }

    static int letKill(char[][] mazeMap, int buff) {
        int state = 0, remainMonster = 0;
        Set<String> candidates = new HashSet<>();
        for (int i = 0; i < mazeMap.length; i++) {
            for (int j = 0; j < mazeMap[i].length; j++) {
                state <<= 1;
                if (mazeMap[i][j] != 'D') {
                    state += 1;
                    if (mazeMap[i][j] != '.')
                        remainMonster++;
                    if (i - 1 >= 0 && mazeMap[i - 1][j] == 'D' || j - 1 >= 0 && mazeMap[i][j - 1] == 'D' || i + 1 < N && mazeMap[i + 1][j] == 'D' || j + 1 < M && mazeMap[i][j + 1] == 'D')
                        candidates.add(i + "" + j);
                }
            }
        }
        if (remainMonster == 0 || state == 0)
            return 0;
        if (dp[buff][state] != Integer.MAX_VALUE)
            return dp[buff][state];
        dp[buff][state] = HP;
        for (String candidate : candidates) {
            int i = candidate.charAt(0) - '0', j = candidate.charAt(1) - '0';
            char curChar = mazeMap[i][j];
            int curBuff = buff - 1, needHP = 0;
            if (mazeMap[i][j] == 'M' || mazeMap[i][j] == 'S') {//attack
                Monster monster = monsterMap.get(i + "" + j);
                int monsterHP = monster.hp;
                while (monsterHP > 0) {
                    monsterHP -= AP;
                    if (curBuff <= 0)
                        needHP += monster.ap;
                    curBuff--;
                }
            }
            if (needHP >= HP)
                continue;
            if (mazeMap[i][j] == 'S')
                curBuff = ROUND;
            else
                curBuff = curBuff <= 0 ? 0 : curBuff;
            mazeMap[i][j] = 'D';
            int minHP = letKill(mazeMap, curBuff);
            mazeMap[i][j] = curChar;
            if (minHP >= HP)
                continue;
            dp[buff][state] = Math.min(dp[buff][state], needHP + minHP);
        }
        return dp[buff][state];
    }
}

#1492 : Parentheses Sequence
这里写图片描述

先统计总的缺少括号数量,每个相反种类括号的位置认为是一个有效的插入点,统计每个插入点之前的最少插入量,记录在least数组中,比如()),缺少一个(,两个)都是有效插入点,第一个)要求在此之前至少插入0个(,第二个)要求在此之前至少插入1个(,所以对应的least数组是[0,1],再如(())))的least数组是[0,0,1,2]。得到least数组后,通过动态的规划的思想来做。
现在以“(())))”为例,仅讨论‘)’比‘(’多的情况:
遍历了least数组,令j表示当前插入点,需要插入的‘(’数,因为least[i]表示当前插入点最少需要插入的‘(’。所以j的取值范围是least[i]~max。(max是需要插入‘(’总数)。既然是dp,那么就需要一个dp 的公式啦!
这里dp[j]表示,当前插入点插入j个‘(’的方法数。所以
tmpdp[j]=dp[0]+dp[1]+…dp[j];
dp=tmpdp;
意思就是当前插入点插入j个‘(’的方法数有下边这么多种方法:
1、前一个插入点插入0个,这个插入点插入j个;
2、前一个插入点插入1个,这个插入点插入(j-1)个;

j+1、前一个插入j个,这个插入点不插入;

由于这里插入的全部都是‘(’,而且都是在两个‘)’之间,所以插入1,2,3…j个‘(’都只有一种方法,例如“()())”,在第一个和第二个‘)’中间插入‘(’,插入一个‘(’,结果都是()(())。
其实计算前一个插入点插入个数的时候,不一定需要从0开始,因为前一个也有至少需要插入几个。这里由于计算每个tmpdp的时候,都是从least[i]开始的,所以前一个插入点的least[i]前面的dp[j]都是0。按照这个方法计算,时间复杂度是O(n^3)。可以在计算dp[0]+dp[1]+…dp[j]的时候,先计算一下,前缀和,然后再来取值,这样就能得到O(n^2)了。
由于上面仅仅讨论的是‘)’比‘(’多的情况。我们可以通过对于原来的字符串取一个镜像,就是翻转,然后将‘(’换成‘)’。然后再算一遍。就能涵盖所有的情况了,最后合并两部分的结果。

Accept代码

import java.util.*;

/**
 * Created by ZhengChaoJie on 2017/4/9/0009.
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String str1 = scanner.nextLine();
        scanner.close();
        Map<String, Integer> part1 = getNeedAndMethods(str1);
        int need1 = part1.get("need"), methods1 = part1.get("methods");
        StringBuffer str2 = new StringBuffer(str1);
        str2.reverse();
        for (int i = 0; i < str2.length(); i++) {
            if (str2.charAt(i) == '(')
                str2.setCharAt(i, ')');
            else
                str2.setCharAt(i, '(');
        }
        Map<String, Integer> part2 = getNeedAndMethods(str2.toString());
        int need2 = part2.get("need"), methods2 = part2.get("methods");
        System.out.println((need1 + need2) + " " + (methods1 * methods2) % 1000000007);
    }

    static Map<String, Integer> getNeedAndMethods(String string) {
        Map<String, Integer> result = new HashMap<>();
        int left = 0, right = 0, need = 0;
        char[] strs = string.toCharArray();
        List<Integer> least = new ArrayList<>();
        List<Integer> tmpLeast = new ArrayList<>();
        for (char ch : strs) {
            if (ch == '(')
                left++;
            else {
                right++;
                if (right - left > need) {
                    need = right - left;
                    tmpLeast.add(need);
                    least.addAll(tmpLeast);
                    tmpLeast.clear();
                } else if (right - left > 0)
                    tmpLeast.add(right - left);
                else
                    tmpLeast.add(0);
            }
        }

        result.put("need", need);
        if (need == 0) {
            result.put("methods", 1);
            return result;
        }
        int[] dp = new int[need + 1];
        dp[0] = 1;
        for (Integer insert : least) {
            int[] preSum = new int[need + 1];
            preSum[0] = dp[0];
            for (int i = 1; i <= need; i++) {
                preSum[i] = (preSum[i - 1] + dp[i]) % 1000000007;
            }
            for (int i = 0; i <= need; i++) {
                if (i < insert)
                    dp[i] = 0;
                else
                    dp[i] = preSum[i];
            }
        }
        result.put("methods", dp[need]);
        return result;
    }
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值