阿里巴巴笔试题

第一题

放牛问题:
假设你家有n头牛(体重各不相同),但是这牛是薛定谔的牛,你不知道它具体是什么颜色,只有牵出来才知道,颜色有m种。然后问你一共有多少种不同的放牛方法。也就是说你可以拉出多少不同状态的牛,只有当牛的数量、体重和颜色完全一样时才视为同一个状态。注意:因为可能结果很大,因此需要模1000000007.
例如:n=3, m=2。
你可以选择一头牛也不牵出来,此时只有1种状态;
你也可以牵出1头牛,则有 C 3 1 C_3^1 C31种可能,牛的颜色可能有2种,此时一共6种状态;
你可以牵出2头牛,则有 C 3 2 C_3^2 C32种可能,牛的颜色可能有 2 2 2^2 22种,此时有12种状态;
你可以牵出3头牛,有1种可能,牛的颜色有 2 3 2^3 23此时有8种状态;
最终加起来一共是27种状态。

解题历程:
首先就是按照题中所给的例子的方法,先计算组合数,然后计算颜色的种类,两者相乘得到状态数,最后相加得到总数。但是这种方法超时。
然后开始改进,突然发现这其实就是二项式定理,结果应该为 ( m + 1 ) n (m+1)^n (m+1)n,但是提交发现溢出,尽管已经对结果求模了。
继续改进,将所有的变量都改为long型,这次结果不溢出了,但是还是超时。
再次改进,使用快速幂方法。时间复杂度一下子降到了log(n)。

代码实现:

/**
 * 计算幂,快速幂
 * @param base 底
 * @param n 指数
 * @return base的n次幂
 * 时间复杂度为logn
 */
public static long pow(long base, long n) {
     
    if (n == 0) return 1;
    long ans = 1;
    long b = base;
    while (n != 0) {
     //n不等于0时继续
        if ((n & 1) != 0) ans = (ans * b) % MOD;//末尾为1,就乘以对应的mi,否则乘1
        //幂指增加
        b = (b * b) % MOD;
        //右移一位
        n = n >> 1;
    }
    return ans;
}

第二题

问题描述:
有一块网格区域,其中遍布着海洋与陆地,每块格子代表一块陆地或者是海洋,如果从海洋到相邻的陆地或者从陆地到相邻的海洋,需要花费5点体力,如果是从海洋到另一个海洋则需要3点体力,陆地到陆地需要2点体力。给定这个区域的布局,以及起点和终点坐标,求出从起点到终点要消耗的最小体力值。

思路:
有两种做法,深度优先(广度优先)和迪杰斯特拉,本来想试试动态规划,发现并不适用。

代码:

import java.util.Scanner;

/**
 * @author FANG
 * @version 1.8
 * @coding UTF-8
 * @date 2020/7/31 19:06
 */
public class One {
     
    private final static long MOD = 1000000007;
    private static int min_force = 0x7fffffff;
    public static void main(String[] args) {
     
        Scanner in = new Scanner(System.in);
//        while (in.hasNext()) {
     
//            long n = in.nextLong();
//            long m = in.nextLong() + 1;
//            System.out.println(pow(m, n));
//        }
//        in.nextByte();
//        int n = in.nextInt();//行数
//        int m = in.nextInt();//列数
//        int num = in.nextInt();//测试例数目
        int n = 4, m = 4, num = 4;
        char[][] land = new char[][]{
     
            {
     
               'C','C','C','S'
            },
            {
     
                'S','S','S','S'
            },
            {
     
               'C','S','C','S'
            },
            {
     
                'S','S','C','C'
            }
        };
//        for (int i = 0; i < n; i++) {
     
//            land[i] = in.next().toCharArray();
//        }
        boolean[][] visited = new boolean[n][m];
        for (int i = 0; i < num; i++) {
     
            int startR = in.nextInt()-1;//起点行号
            int startC = in.nextInt()-1;//起点列号
            int endR = in.nextInt()-1;//终点行号
            int endC = in.nextInt()-1;//终点列号
            dfs(land, visited, startR, startC, endR, endC, 0);
            System.out.println(min_force);
            min_force = 0x7fffffff;

            System.out.println(minForce(land, startR, startC, endR, endC));

        }


    }

    /**
     * 计算幂,快速幂
     * @param base 底
     * @param n 指数
     * @return base的n次幂
     * 时间复杂度为logn
     */
    public static long pow(long base, long n) {
     
        if (n == 0) return 1;
        long ans = 1;
        long b = base;
        while (n != 0) {
     //n不等于0时继续
            if ((n & 1) != 0) ans = (ans * b) % MOD;//末尾为1,就乘以对应的mi,否则乘1
            //幂指增加
            b = (b * b) % MOD;
            //右移一位
            n = n >> 1;
        }
        return ans;
    }

    /**
     * 使用深度优先搜索地图找到花费体力最小的路径
     * @param land 地形图
     * @param curi 起点行号
     * @param curj 起点列号
     * @param ei 终点行号
     * @param ej 终点列号
     * @param cur_force 走到当前点消耗的体力
     */
    public static void dfs(char[][] land, boolean[][] visited, int curi, int curj, int ei, int ej, int cur_force) {
     
        //走到了终点
        if (curi == ei && curj == ej) {
     //走到了终点
            //更新最小体力值
//            System.out.println(cur_force);
            min_force = Math.min(min_force, cur_force);
            return;
        }
        //如果当前体力消耗已经大于等于当前最小值,则可以不用走了
        if (cur_force >= min_force) return;

        char ch = land[curi][curj];
        //向上走
        if (curi - 1 >= 0 && !visited[curi-1][curj]) {
     //要保证不能越界,并且未被走过
            //
            visited[curi-1][curj] = true;
            char c = land[curi-1][curj];
            int cost;
            if (ch != c) cost = 5;
            else if (ch == 'C') cost = 2;
            else cost = 3;
            //开始深度优先搜索
            dfs(land, visited, curi-1, curj, ei, ej, cur_force + cost);

            //恢复状态
            visited[curi-1][curj] = false;
        }

        //向下走
        if (curi + 1 < land.length && !visited[curi+1][curj]) {
     //要保证不能越界,并且未被走过
            //
            visited[curi+1][curj] = true;
            char c = land[curi+1][curj];
            int cost;
            if (ch != c) cost = 5;
            else if (ch == 'C') cost = 2;
            else cost = 3;
            //开始深度优先搜索
            dfs(land, visited, curi+1, curj, ei, ej, cur_force + cost);

            //恢复状态
            visited[curi+1][curj] = false;
        }

        //向左走
        if (curj - 1 >= 0 && !visited[curi][curj-1]) {
     //要保证不能越界,并且未被走过
            //
            visited[curi][curj-1] = true;
            char c = land[curi][curj-1];
            int cost;
            if (ch != c) cost = 5;
            else if (ch == 'C') cost = 2;
            else cost = 3;
            //开始深度优先搜索
            dfs(land, visited, curi, curj-1, ei, ej, cur_force + cost);

            //恢复状态
            visited[curi][curj-1] = false;
        }

        //向左走
        if (curj + 1 < land[0].length && !visited[curi][curj+1]) {
     //要保证不能越界,并且未被走过
            //
            visited[curi][curj+1] = true;
            char c = land[curi][curj+1];
            int cost;
            if (ch != c) cost = 5;
            else if (ch == 'C') cost = 2;
            else cost = 3;
            //开始深度优先搜索
            dfs(land, visited, curi, curj+1, ei, ej, cur_force + cost);

            //恢复状态
            visited[curi][curj+1] = false;
        }
    }

    /**
     * 使用迪杰斯特拉最短路算法求解
     * @param land 海陆分布图
     * @param si 起点坐标
     * @param sj 起点坐标
     * @param ei 终点坐标
     * @param ej 终点坐标
     * @return 最小体力值
     */
    public static int minForce(char[][] land, int si, int sj, int ei, int ej) {
     
        int n = land.length;
        int m = land[0].length;
        //初始化最小体力数组,minF[i][j]表示从起点到该点花的最小体力
        int[][] minF = new int[n][m];
        boolean[][] visited = new boolean[n][m];
        visited[si][sj] = true;
        //初始化minF数组
        for (int i = 0; i < n; i++) {
     
            for (int j = 0; j < m; j++) {
     
                //赋初值,正无穷大
                minF[i][j] = Integer.MAX_VALUE;

                //如果是它自己,则改为0
                if (i == si && j == sj) minF[i][j] = 0;
                //上邻居
                if (i == si-1 && j == sj) {
     
                    char neib = land[si][sj];
                    char ch = land[i][j];
                    int cost;
                    if (ch != neib) cost = 5;
                    else if (ch == 'C') cost = 2;
                    else cost = 3;
                    minF[i][j] = cost;
                }
//              下邻居
                if (i == si+1 && j == sj) {
     
                    char neib = land[si][sj];
                    char ch = land[i][j];
                    int cost;
                    if (ch != neib) cost = 5;
                    else if (ch == 'C') cost = 2;
                    else cost = 3;
                    minF[i][j] = cost;
                }
                //左邻居
                if (i == si && j == sj-1) {
     
                    char neib = land[si][sj];
                    char ch = land[i][j];
                    int cost;
                    if (ch != neib) cost = 5;
                    else if (ch == 'C') cost = 2;
                    else cost = 3;
                    minF[i][j] = cost;
                }
                // 右邻居
                if (i == si && j == sj+1) {
     
                    char neib = land[si][sj];
                    char ch = land[i][j];
                    int cost;
                    if (ch != neib) cost = 5;
                    else if (ch == 'C') cost = 2;
                    else cost = 3;
                    minF[i][j] = cost;
                }
            }
        }

        //开始迭代找次短路径(次小体力消耗路径)
        for (int k = 1; k < n*m; k++) {
     

            //找未访问集合中路径最短的
            int min = Integer.MAX_VALUE;
            int min_i = -1, min_j = -1;
            for (int i = 0; i < n; i++) {
     
                for (int j = 0; j < m; j++) {
     
                    //未访问过结点中最短的
                    if (!visited[i][j]) {
     
//                        System.out.println(minF[i][j]);
                        if (minF[i][j] < min) {
     
                            min = minF[i][j];
                            min_i = i;
                            min_j = j;
//                            System.out.println(min);
                        }
                    }
                }
            }

            //找到后添加进访问集合中
            visited[min_i][min_j] = true;

            //然后用这个点去更新未被添加进访问集合中的其他点,
            // 因为只有它的邻居可能被更新,所以只需要看它的邻居即可。
            //上邻居存在且不在最短路集合中
            if (min_i - 1 >= 0 && !visited[min_i-1][min_j]) {
     
                char ch = land[min_i][min_j];
                char neib = land[min_i-1][min_j];
                int cost;
                if (ch != neib) cost = 5;
                else if (ch == 'C') cost = 2;
                else cost = 3;
                //更新其值
                minF[min_i-1][min_j] = Math.min(minF[min_i-1][min_j], minF[min_i][min_j] + cost);
            }

            //下邻居存在且不在最短路集合中
            if (min_i + 1 < n && !visited[min_i+1][min_j]) {
     
                char ch = land[min_i][min_j];
                char neib = land[min_i+1][min_j];
                int cost;
                if (ch != neib) cost = 5;
                else if (ch == 'C') cost = 2;
                else cost = 3;
                //更新其值
                minF[min_i+1][min_j] = Math.min(minF[min_i+1][min_j], minF[min_i][min_j] + cost);
            }

            //左邻居存在且不在最短路集合中
            if (min_j - 1 >= 0 && !visited[min_i][min_j-1]) {
     
                char ch = land[min_i][min_j];
                char neib = land[min_i][min_j-1];
                int cost;
                if (ch != neib) cost = 5;
                else if (ch == 'C') cost = 2;
                else cost = 3;
                //更新其值
                minF[min_i][min_j-1] = Math.min(minF[min_i][min_j-1], minF[min_i][min_j] + cost);
            }

            //右邻居存在且不在最短路集合中
            if (min_j + 1 < m && !visited[min_i][min_j+1]) {
     
                char ch = land[min_i][min_j];
                char neib = land[min_i][min_j+1];
                int cost;
                if (ch != neib) cost = 5;
                else if (ch == 'C') cost = 2;
                else cost = 3;
                //更新其值
                minF[min_i][min_j+1] = Math.min(minF[min_i][min_j+1], minF[min_i][min_j] + cost);
            }

        }
        return minF[ei][ej];
    }
}

你可能感兴趣的:(阿里巴巴笔试题)