【2021春招】阿里笔试真题

少壮不努力,老大徒伤悲!

ali

2021.3.6

1411. 给 N x 3 网格图涂色的方案数
815. 公交路线

1

你有一个 n x 3 的网格图 grid ,你需要用 红,黄,绿 三种颜色之一给每一个格子上色,且确保相邻格子颜色不同(也就是有相同水平边或者垂直边的格子颜色不同)。给你网格图的行数 n 。
请你返回给 grid 涂色的方案数。由于答案可能会非常大,请你返回答案对 10^9 + 7 取余的结果。
在这里插入图片描述

找规律,动态规划

class Solution {
    
    
    static final int MOD = 1000000007;

    public int numOfWays(int n) {
    
    
        long fi0 = 6, fi1 = 6;
        for (int i = 2; i <= n; ++i) {
    
    
            long newFi0 = (2 * fi0 + 2 * fi1) % MOD;
            long newFi1 = (2 * fi0 + 3 * fi1) % MOD;
            fi0 = newFi0;
            fi1 = newFi1;
        }
        return (int) ((fi0 + fi1) % MOD);
    }
}

2

给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶。

例如,路线 routes[0] = [1, 5, 7] 表示第 0 辆公交车会一直按序列 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> … 这样的车站路线行驶。
现在从 source 车站出发(初始时不在公交车上),要前往 target 车站。 期间仅可乘坐公交车。

求出 最少乘坐的公交车数量 。如果不可能到达终点车站,返回 -1 。

BFS:广度优先遍历,

import java.util.Arrays;

class Solution {
    
    
    public int numBusesToDestination(int[][] routes, int source, int target) {
    
    
		int maxStationNum = 0;//最大车站编号
        //统计最大车站编号
		for(int[] route:routes){
    
    
			for(int num: route){
    
    
				maxStationNum = Math.max(maxStationNum, num);
			}
		}
        //终点车站比最大的大,即不存在,直接返回
        if(target > maxStationNum){
    
    
            return -1;
        }
        //用来存储每个车站距离source站换车次数
		int[] dp = new int[maxStationNum+1];
		Arrays.fill(dp, maxStationNum+1);//初始设置为不可达,maxStationNum+1表示不可达
        //设置起点可达需要0次
		dp[source] = 0;

        //用来标记车数组中是否发生修改
		boolean updateFlag = true;
        //广度优先遍历
		while(updateFlag){
    
    
            //每一轮遍历车数组
			for(int[] route:routes){
    
    
                //统计当前车次中可达终点的最小次数
				int min = maxStationNum+1;
				for(int num: route){
    
    
					if(dp[num] < min){
    
    
						min = dp[num];
					}
				}
                //根据最小次数,更新同一个车次中,其他站的最小次数
				for(int num: route){
    
    
					if(dp[num] > min+1){
    
    
						dp[num] = min+1;
						updateFlag = false;//标志数组发生修改
					}
				}
			}
            //如果发生修改,即所有数组中站点修改完成,为false
            //如果没有发生修改,即所有数组中站点修改完成,为true
			updateFlag = !updateFlag;//如果全部修改完成,终止while循环
		}
        //返回到达target的最小次数
		if(dp[target] < maxStationNum+1){
    
    
			return dp[target];
		}
		return -1;
	}	
}

2021.3.8

1539. 第 k 个缺失的正整数
879. 盈利计划

1

给你一个 严格升序排列 的正整数数组 arr 和一个整数 k 。

请你找到这个数组里第 k 个缺失的正整数。

模拟计数的过程

class Solution {
    
    
    public int findKthPositive(int[] arr, int k) {
    
    
        int count = 0;
        int miss = 0;
        for(int i = 0; i < arr.length; i++){
    
    
            count++;
            while(count < arr[i]){
    
    
                miss++;
                if(miss == k){
    
    
                    return count;
                }
                count++;
            }
        }
        return arr[arr.length-1]+k-miss;
    }
}

2

集团里有 n 名员工,他们可以完成各种各样的工作创造利润。

第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与。如果成员参与了其中一项工作,就不能参与另一项工作。

工作的任何至少产生 minProfit 利润的子集称为盈利计划。并且工作的成员总数最多为 n 。

有多少种计划可以选择?因为答案很大,所以 返回结果模 10^9 + 7 的值。

dp,背包问题类似

class Solution {
    
    
    public int profitableSchemes(int G, int P, int[] group, int[] profit) {
    
    
        int mod = 1000_000_007;
        int[][] dp = new int[G + 1][P + 1];
        //dp[i][j]表示,分配j个工人收益i的收益计划数量
        //dp数组从最后一行向前,
        
        dp[0][0] = 1;
        int taskNum = group.length;
        //动态规划
        for (int i = 0; i < taskNum; i++) {
    
    //首先是遍历每一个任务
            int workerNum = group[i];//当前任务的工人数量
            int workProfit = profit[i];//当前任务带来的收益
            for (int j = G; j >= workerNum; j--) {
    
    
                for (int k = P; k >= 0; k--) {
    
    
                    int indexK = 0;
                    if(workProfit < k){
    
    
                        indexK = k - workProfit;
                    }
                    dp[j][k] = (dp[j][k] + dp[j-workerNum][indexK])%mod;
                }
            }
        }
		
        int sum = 0;
        for (int i = 0; i < G+1; i++){
    
    
            sum = (sum + dp[i][P])%mod;
        }
        return sum;
    }
}

2021.3.10

第一题:没有找到leetcode
1388. 3n 块披萨

1

题目描述:

模拟题,只能朝着一个方向走,撞墙停止,四个方向
读取输入不太习惯,nextInt完需要nextLine读掉\n,-_-
@表示起点,#表示墙,输入东南西北方向
input:
3 4 4
@…
.#…

EAST
SOUTH
WEST
NORTH
output:
1 3

2

给你一个披萨,它由 3n 块不同大小的部分组成,现在你和你的朋友们需要按照如下规则来分披萨:

你挑选 任意 一块披萨。
Alice 将会挑选你所选择的披萨逆时针方向的下一块披萨。
Bob 将会挑选你所选择的披萨顺时针方向的下一块披萨。
重复上述过程直到没有披萨剩下。
每一块披萨的大小按顺时针方向由循环数组 slices 表示。

请你返回你可以获得的披萨大小总和的最大值。
在这里插入图片描述

环状3n序列当中选择n个不相邻的数,使得数值和最大

class Solution {
    
    
    public int maxSizeSlices(int[] slices) {
    
    
        int[] slices1 = new int[slices.length - 1];
        System.arraycopy(slices, 1, slices1, 0, slices.length - 1);
        int[] slices2 = new int[slices.length - 1];
        System.arraycopy(slices, 0, slices2, 0, slices.length - 1);
        int ans1 = calculate(slices1);
        int ans2 = calculate(slices2);
        return Math.max(ans1, ans2);
    }

    public int calculate(int[] slices) {
    
    
        int n = slices.length;//序列长度是原本序列长度-1,可选择披萨数量少1
        int choose = (n + 1) / 3;//因为是去掉了头尾计算的,所以可选择披萨要加1
        int[][] dp = new int[n + 1][choose + 1];
        for (int i = 1; i <= n; ++i) {
    
    
            for (int j = 1; j <= choose; ++j) {
    
    
                dp[i][j] = Math.max(dp[i - 1][j], (i - 2 >= 0 ? dp[i - 2][j - 1] : 0) + slices[i - 1]);
            }
        }
        return dp[n][choose];
    }
}

笔记

  1. 关于环状:分别计算[0,last-1],[1,last]的值,求最大
  2. 关于与打家劫舍2区别:在总数为n的序列中,打劫2能偷 n / 2 间;Pizza能拿 n / 3 片。

2021.3.12

1

有向图的BFS,
告知一个有向图,问a到b的最短距离

解法:有向图Dijkstra

2

n*n棋盘中,有“车”,移动“ 车”到对角线位置,其中移动过程中不能被其他“车”吃掉,问移动最少次数
C. Peaceful Rooks

猜你喜欢

转载自blog.csdn.net/qq_39457586/article/details/114489390