阿里暑期实习笔试题

阿里暑期实习笔试题

阿里暑期实习的笔试,3月6日的第一场据说比较难,两道 leetcode hard题。


3.9 更新

找到问题了,问题出在 auto&& [stop, step] = q.front(); q.pop(); 这两句上,我们使用的是一个引用,但是后来这个元素被 pop 释放掉了,所以会报内存错误。
使用 auto& [stop, step] 也会出错,因为也是引用类型;只有使用 auto [stop, step] 做拷贝赋值才不会引起内存错误。

LeetCode 1411. Number of Ways to Paint N × 3 Grid

第一题感觉思路还是很直观的,注意数据的溢出问题就行了,1E9 * 2 不会溢出,1E9 * 3 就会溢出了。我这里没有把数据类型提升到 int64_t,不然的话可以只做一次取余,毕竟 int64_t 之下 1E9 * 5 也溢出不了。

题目描述

You have a grid of size n x 3 and you want to paint each cell of the grid with exactly one of the three colors: Red, Yellow, or Green while making sure that no two adjacent cells have the same color (i.e., no two cells that share vertical or horizontal sides have the same color).

Given n the number of rows of the grid, return the number of ways you can paint this grid. As the answer may grow large, the answer must be computed modulo 109 + 7.

Example 1:

Input: n = 1
Output: 12
Explanation: There are 12 possible way to paint the grid as shown.

Example 2:

Input: n = 2
Output: 54

Example 3:

Input: n = 3
Output: 246

Example 4:

Input: n = 7
Output: 106494

Example 5:

Input: n = 5000
Output: 30228214

Constraints:

  • n == grid.length
  • grid[i].length == 3
  • 1 <= n <= 5000

解题思路

这是一道显而易见的 DP 题目,下一层的涂色方案仅依赖于上一层,有点类似于那道矩形覆盖题:用2x1的小矩形无重叠地覆盖2xn的大矩形(斐波那契数列嘛)。
需要注意的是,对于上一层的涂色可以分为两类,各自对应不同的下一层涂色方案:

  • 【红绿红】这种两侧颜色相同的,可以推出3种两侧相同的方案、2种两侧颜色不同的方案;
  • 【红绿黄】这种两侧颜色不同的,可以推出2种两侧相同的方案、2种两侧颜色不同的方案;

然后就是具体的实现问题了,是保存每一步的状态还是压缩状态,是线性推导还是快速矩阵幂(可以达到对数时间复杂度)。

参考代码

直接使用简单的不保存状态的线性时间 DP 方案:

/*
 * @lc app=leetcode id=1411 lang=cpp
 *
 * [1411] Number of Ways to Paint N × 3 Grid
 */

// @lc code=start
class Solution {
public:
    int numOfWays(int n) {
        assert(n >= 1);

        constexpr int MOD = 1E9 + 7;
        int same = 6, diff = 6; // 6 同 6 异
        for (int i = 2; i <= n; i++) {
            int s = same, d = diff;
            same = (((s+s)%MOD + s)%MOD + (d+d)%MOD) % MOD;
            diff = ((s+s)%MOD + (d+d)%MOD) % MOD;
        } // 同 -> 3同2异;异 -> 2同2异
        return (same + diff) % MOD;
    }
};
// @lc code=end

LeetCode 815. Bus Routes

第二题就不那么好想好写了,想到思路做试验再改正可能时间就来不及了。而且一旦遇到 heap-use-after-free 之类的奇怪bug基本也就完蛋了。

题目描述

You are given an array routes representing bus routes where routes[i] is a bus route that the ith bus repeats forever.

  • For example, if routes[0] = [1, 5, 7], this means that the 0th bus travels in the sequence 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... forever.

You will start at the bus stop source (You are not on any bus initially), and you want to go to the bus stop target. You can travel between bus stops by buses only.

Return the least number of buses you must take to travel from source to target. Return -1 if it is not possible.

Example 1:

Input: routes = [[1,2,7],[3,6,7]], source = 1, target = 6
Output: 2
Explanation: The best strategy is take the first bus to the bus stop 7, then take the second bus to the bus stop 6.

Example 2:

Input: routes = [[7,12],[4,5,15],[6],[15,19],[9,12,13]], source = 15, target = 12
Output: -1

Constraints:

  • 1 <= routes.length <= 500.
  • 1 <= routes[i].length <= 105
  • All the values of routes[i] are unique.
  • sum(routes[i].length) <= 105
  • 0 <= routes[i][j] < 106
  • 0 <= source, target < 106

解题思路

看成一个图的问题,使用 BFS 解决。
图中有两类结点,分别是 bus 和 stop,可以看作是一个二部图 —— 左边是 bus 右边是 stop。现在要解决的是从一个stop到另一个 stop 需要经过几个 bus。
我们首先先把图的另一半建立,然后再一边 BFS 一边计步就可以了。

参考代码

AC 代码:

/*
 * @lc app=leetcode id=815 lang=cpp
 *
 * [815] Bus Routes
 */

// @lc code=start
class Solution {
public:
    // 公交换乘问题
    int numBusesToDestination(vector<vector<int>>& routes, int S, int T) {
        if (S == T) return 0;

        unordered_map<int, vector<int>> stop2bus;
        for (int i = 0; i < routes.size(); ++i) {
            for (int stop : routes[i]) {
                stop2bus[stop].push_back(i);
            }
        }
        vector<bool> visited(routes.size(), false);
        queue<int> q;
        q.push(S);
        int buses = 0;

        while (!q.empty()) {
            int size = q.size();
            ++buses;
            while (size--) {
                int curr = q.front(); q.pop();
                for (int bus : stop2bus[curr]) {
                    if (visited[bus]) continue;
                    visited[bus] = true;
                    for (int stop : routes[bus]) {
                        if (stop == T) return buses;
                        if (stop == curr) continue;
                        q.push(stop);
                    }
                }
            }
        }
        return -1;
    }
};
// @lc code=end

下面几乎完全相同的代码,却会报 Runtime Error : AddressSanitizer: heap-use-after-free 也是奇怪。上一次遇到这种问题,也是用unorder_map的时候。

/*
 * @lc app=leetcode id=815 lang=cpp
 *
 * [815] Bus Routes
 */

// @lc code=start
class Solution {
public:
    // 公交换乘问题
    int numBusesToDestination(vector<vector<int>>& routes, int source, int target) {
        if (source == target) return 0;

        size_t nbus = routes.size();
        unordered_map<int, vector<int>> buses; // stop -> routes
        for (int i = 0; i < nbus; i++) {
            for (int stop: routes[i]) {
                buses[stop].push_back(i);
            }
        }
        queue<pair<int, int>> q; // 存放站台
        vector<bool> vis(nbus, false); // 这班车坐过了
        q.push({source, 0}); // 地点、坐车几次
        while (!q.empty()) {
            auto&& [stop, step] = q.front();
            q.pop();
            // if (stop == target) return step;
            for (int b : buses[stop]) {
                if (vis[b]) continue;
                vis[b] = true;
                for (int s : routes[b]) {
                    if (s == stop) continue;
                    if (s == target) return step+1;
                    q.push({s, step+1});
                }
            }
        }

        return -1;
    }
};
// @lc code=end
posted @ 2021-03-08 18:46  与MPI做斗争  阅读(537)  评论(0编辑  收藏  举报