Coding Kick Start

Google Kick Start 2019 C轮 题解

查阅更多的题解,请点击
GitHub传送门

A.Wiggle Walk

Banny has just bought a new programmable robot. Eager to test his coding skills, he has placed the robot in a grid of squares with R rows (numbered 1 to R from north to south) and C columns (numbered 1 to C from west to east). The square in row r and column c is denoted (r, c).

Initially the robot starts in the square (S_R, S_C). Banny will give the robot N instructions. Each instruction is one of N, S, E or W, instructing the robot to move one square north, south, east or west respectively.

If the robot moves into a square that it has been in before, the robot will continue moving in the same direction until it reaches a square that it has not been in before. Banny will never give the robot an instruction that will cause it to move out of the grid.

Can you help Banny determine which square the robot will finish in, after following the N instructions?

Input

The first line of the input gives the number of test cases, T. T test cases follow. Each test case starts with a line containing the five integers N, R, C, S_R and S_C, the number of instructions, the number of rows, the number of columns, the robot’s starting row and starting column, respectively.

Then, another line follows containing a single string of N characters; the i-th of these characters is the i-th instruction Banny gives the robot (one of N, S, E or W, as described above).

Output

For each test case, output one line containing Case #x: r c, where x is the test case number (starting from 1), r is the row the robot finishes in and c is the column the robot finishes in.

Limits

Memory limit: 1GB. 1 ≤ T ≤ 100. 1 ≤ R ≤ 5 × 104. 1 ≤ C ≤ 5 × 104. 1 ≤ SR ≤ R. 1 ≤ SC ≤ C. The instructions will not cause the robot to move out of the grid.

Test set 1 (Visible)

Time limit: 20 seconds. 1 ≤ N ≤ 100.

Test set 2 (Hidden)

Time limit: 60 seconds. 1 ≤ N ≤ 5 × 104.

Solution

题目大意

题目大致是说有一个R*C的棋盘,给定一个起点,一个机器人从起点出发,输出该机器人经n步最后到达的位置。机器人每一步有四个方向:E-向右,W-向左,N-向上,S-向下。机器人n步的行走方式从外部输入,题目保证机器人不会走出棋盘。其中若机器人某次达到的位置为之前到过的位置,则在当前方向继续前进至第一个之前未到达的位置。

解法

最朴素的想法就是按照n步中每步的指示去走,并且记录每次访问的位置。该算法是O(n^2)time的: 注意在最坏的情况下,不妨设机器人行走序列为:WEWEWEWEWE……,其中WE共出现n/2次(注意机器人每次需要到达之前未到达的位置):

  • 对于第一个WE,机器人向左走1步,向右时需要走2步
  • 对于第二个WE,机器人向左走3步,向右时需要走4步
  • ……

所以机器人总共需要走1+2+3+\cdots+n=(1+n)n/2步,上述算法是O(n^2)time

在上述过程中,对算法性能影响最大的是:对之前已走过路径的重复

假设我们在机器人左右移动时,已知到该行所有已被访问的位置区间,那么可以一步完成本次移动:如当前处于(7,3),第7行中第3列到第11列的位置都已访问,机器人下一步行走方向为向右,则可以知道机器人下一次到达的位置应当为(7,12).

对于上述介绍的已被访问的位置区间,可以称之为一个interval,那么对于棋盘的行和列,维护一个interval list,就可以避免对之前已走过路径的重复。由于希望有较好的性能,那么对于一行/列的interval list,希望有高效的:

  • interval插入
  • interval查找
  • interval合并

这里可以采用hashTable来实现。其中麻烦的是interval的合并,这里以行为例:设机器人在某时刻处于位置(m,n),那么对于第m行的interval list,只有以下三种情况:

  • 存在interval[s_1, m-1]和[m+1,e_2],那么需要合并得到[s_1,e_2]
  • 仅存在interval[s_1, m-1]或[m+1,e_2],那么需要合并得到[s_1,m]或[m,e_2]
  • 不需要合并interval,直接插入[m,m]

由于采用了hash Table,该解法是O(n) time的。(不考虑hash碰撞的情况)

#include <iostream>
#include <unordered_map>
#include <vector>

using namespace std;

vector<unordered_map<int, int>> rowsStart, rowsEnd, colsStart, colsEnd;

void insert(int x, int y)
{
    bool l = rowsEnd[x].count(y - 1), r = rowsStart[x].count(y + 1);
    if (l && r)
    {
        int ls = rowsEnd[x][y - 1], re = rowsStart[x][y + 1];
        rowsStart[x][ls] = re, rowsEnd[x][re] = ls;
    }
    else if (l)
    {
        int ls = rowsEnd[x][y - 1];
        rowsStart[x][ls] = y, rowsEnd[x][y] = ls;
    }
    else if (r)
    {
        int re = rowsStart[x][y + 1];
        rowsEnd[x][re] = y, rowsStart[x][y] = re;
    }
    else
    {
        rowsEnd[x][y] = rowsStart[x][y] = y;
    }

    l = colsEnd[y].count(x - 1), r = colsStart[y].count(x + 1);
    if (l && r)
    {
        int ls = colsEnd[y][x - 1], re = colsStart[y][x + 1];
        colsStart[y][ls] = re, colsEnd[y][re] = ls;
    }
    else if (l)
    {
        int ls = colsEnd[y][x - 1];
        colsStart[y][ls] = x, colsEnd[y][x] = ls;
    }
    else if (r)
    {
        int re = colsStart[y][x + 1];
        colsEnd[y][re] = x, colsStart[y][x] = re;
    }
    else
    {
        colsEnd[y][x] = colsStart[y][x] = x;
    }
}

pair<int, int> calLocation(int m, int n, int x, int y, const string &instructions)
{
    rowsStart.clear();
    rowsEnd.clear();
    colsStart.clear();
    colsEnd.clear();
    rowsStart.resize(m + 1);
    rowsEnd.resize(m + 1);
    colsStart.resize(n + 1);
    colsEnd.resize(n + 1);

    rowsStart[x][y] = y;
    rowsEnd[x][y] = y;
    colsStart[y][x] = x;
    colsEnd[y][x] = x;

    for (auto item : instructions)
    {
        switch (item)
        {
        case 'E':
            if (rowsStart[x].count(y + 1))
                y = rowsStart[x][y + 1] + 1;
            else
                y++;
            break;
        case 'W':
            if (rowsEnd[x].count(y - 1))
                y = rowsEnd[x][y - 1] - 1;
            else
                y--;
            break;
        case 'S':
            if (colsStart[y].count(x + 1))
                x = colsStart[y][x + 1] + 1;
            else
                x++;
            break;
        case 'N':
            if (colsEnd[y].count(x - 1))
                x = colsEnd[y][x - 1] - 1;
            else
                x--;
            break;
        }
        insert(x, y);
    }
    return make_pair(x, y);
}

int main()
{
    int t = 0;
    cin >> t;
    for (int i = 1; i <= t; ++i)
    {
        int n, r, c, s_r, s_c;
        string instructions;
        cin >> n >> r >> c >> s_r >> s_c >> instructions;

        auto ret = calLocation(r, c, s_r, s_c, instructions);
        cout << "Case #" << i << ": " << ret.first << "  " << ret.second << endl;
    }
    return 0;
}

B.Circuit Board

Arsh recently found an old rectangular circuit board that he would like to recycle. The circuit board has R rows and C columns of squares.

Each square of the circuit board has a thickness, measured in millimetres. The square in the r-th row and c-th column has thickness Vr,c. A circuit board is good if in each row, the difference between the thickest square and the least thick square is no greater than K.

Since the original circuit board might not be good, Arsh would like to find a good subcircuit board. A subcircuit board can be obtained by choosing an axis-aligned subrectangle from the original board and taking the squares in that subrectangle. Arsh would like your help in finding the number of squares in the largest good subrectangle of his original board.

Input The first line of the input gives the number of test cases, T. T test cases follow. Each test case begins with one line containing three integers R, C and K, the number of rows, the number of columns, and the maximum difference in thickness allowed in each row.

Then, there are R more lines containing C integers each. The c-th integer on the r-th line is Vr, c, the thickness of the square in the r-th row and c-th column.

Output For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the maximum number of squares in a good subrectangle.

Limits Time limit: 15 seconds per test set. Memory limit: 1GB. 1 ≤ T ≤ 50. 1 ≤ R ≤ 300. 1 ≤ C ≤ 300. 0 ≤ Vi, j ≤ 103 for all i, j.

Test set 1 (Visible) K = 0.

Test set 2 (Hidden) 0 ≤ K ≤ 103.

Solution

题目大意

题目大致是说有一个r\times c的矩阵,代表一个电路板,矩阵中每一个元素是电路板在该处的厚度。现在希望找到一个最大子矩阵的面积,矩阵每一行厚度之差不能超过给定的约束k

解法 O(cr^2) time

若给定子矩阵的左边界,再将子矩阵转置,该问题就是一个经典问题:求直方图的最大矩阵面积-LeetCode 84。具体的解释可以点击84. Largest Rectangle in Histogram(hard)

从左至右,选取一列作为矩阵的左边界,是O(c) time的,对于每一列:

  • 求直方图的height数组:O(r^2) time
  • 利用单调栈求最大矩阵面积:O(r) time

所以该算法是O(cr^2) time的。 Github传送门

#include <algorithm>
#include <iostream>
#include <stack>
#include <vector>

using namespace std;

int largestRectangleArea(vector<int> &heights)
{
    if (heights.size() == 1)
        return heights[0];
    heights.emplace_back(-1);
    int n = heights.size();
    int maxArea = 0, i = 0;
    stack<int> indexs;
    while (i < n)
    {
        if (indexs.empty() || heights[i] >= heights[indexs.top()])
            indexs.push(i++);
        else
        {
            int j = indexs.top();
            indexs.pop();
            int cur = heights[j] * (indexs.empty() ? i : (i - indexs.top() - 1));
            maxArea = max(maxArea, cur);
        }
    }
    return maxArea;
}

int cal(int m, int n, const vector<vector<int>> &inputs, int k)
{
    int maxArea = 0;
    vector<int> heights(m, 0);
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < m; ++j)
        {
            int pos = i;
            int curMin = inputs[j][pos], curMax = inputs[j][pos];
            pos++;
            while (pos < n)
            {
                if (inputs[j][pos] > curMax)
                    curMax = inputs[j][pos];
                else if (inputs[j][pos] < curMin)
                    curMin = inputs[j][pos];
                if (curMax - curMin > k)  
                    break;
                pos++;
            }
            heights[j] = pos - i;
        }
        maxArea = max(maxArea, largestRectangleArea(heights));
    }
    return maxArea;
}

int main()
{
    int t = 0;
    cin >> t;
    for (int i = 1; i <= t; ++i)
    {
        int r, c, k;
        cin >> r >> c >> k;
        vector<vector<int>> inputs(r, vector<int>(c));
        for (int i = 0; i < r; ++i)
        {
            for (int j = 0; j < c; ++j)
            {
                cin >> inputs[i][j];
            }
        }
        auto ret = cal(r, c, inputs, k);
        cout << "Case #" << i << ": " << ret << endl;
    }
    return 0;
}

C.Catch Some

Problem Bundle is an animal researcher and needs to go observe K dogs. She lives on a horizontal street marked at metre increments with consecutive numbers 0, 1, 2, 3 and so on. She begins in her home, which is at position 0. There are also N dogs on the street. The i-th dog is Pi metres to the right of her home on the street (multiple dogs can share the same position).

Dogs come in different colors, which are denoted by positive integers. The i-th animal is of color Ai.

If Bundle is at her home, she can change the current color of her shirt. This is important since the dogs are very shy! Bundle can only observe a dog if she is at the same position as that dog, and is wearing a shirt of the same color as the dog.

It takes Bundle one second to move one metre to the left or right on the street. It takes her no time to change shirts or observe a dog.

What is the least amount of time it will take Bundle to observe K dogs? Note that she does not have to return home after observing K dogs.

Input The first line of the input gives the number of test cases, T. T test cases follow. Each testcase begins with a line containing the two integers N and K, the number of dogs on the number line and the number of dogs Bundle needs to observe, respectively. The second line contains N integers, the i-th of which is Pi, the position of the i-th dog. The third line contains N integers, the i-th of which is Ai, the color of the i-th dog.

Output For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the least time Bundle needs to observe K dogs.

Limits Time limit: 30 seconds per test set. Memory limit: 1GB. 1 ≤ T ≤ 100. 1 ≤ K ≤ N. 1 ≤ Ai ≤ 1000. 1 ≤ Pi ≤ 105.

Test set 1 (Visible) 1 ≤ N ≤ 50.

Test set 2 (Hidden) 1 ≤ N ≤ 1000.

Solution

题目大意

在一条一维沿x轴正方向的数轴上有N条狗,每条狗在不同的位置且是不同的颜色。Bundle想要观察这些狗,规则是当且仅当 Bundle 身上穿的T恤颜色和狗的颜色相同时,狗才会出现。Bundle只能在家里换T恤,Bundle的家在x=0的位置。假如Bundle想要观察K条狗,最短的时间是多少?其中,每移动(向左向右)消耗1个单位时间,换T恤不消耗任何时间

解法 O(N^2) time

这道题比较难,先观察题目,可以从题目中得出以下结论

  • 对于每种颜色,Bundle只会穿一次,不会反复换
  • 若Bundle观察到位置j的某颜色的狗,则在此之前的同颜色的狗也一定全部被观察
  • 不考虑最后一次观察(即不回家),观察某只狗的时间为其位置乘以2(note: 在该狗之前的同颜色的狗也在该时间内被观察)

有这样的观察,在不考虑最后一次观察的情况下,令dp[i][j]代表在颜色1到颜色i中,观察了j条狗所用最短时间,则可以得到以下的状态转移方程: {dp}[i][j] = \min_{k\le\text{count}(d_i)}{\text{dp}[i-1][j-k]+2\times d_{i,k}} 其中d_i代表颜色为i狗的个数,d_{i,k}为颜色为i的第k条狗的位置(从左至右排序)。

如果忽略最后一次观察,那么所求解为dp[C][K],C为狗的颜色总数。dp[C][K]代表在C种颜色狗中观察K条狗所用最短时间,该方法是O(N^2)time的。考虑最后一次观察的话,需要枚举C种颜色,每次选一个颜色作为最后观察的,其余颜色同上,该解法是O(N^3)time的。

对于上述过程,可以进行优化:即对原有状态扩展

  • dp[i][j][0]代表在颜色1到颜色i中,观察了j条狗所用最短时间,其中没有包含最后一次观察的颜色
  • dp[i][j][1]代表在颜色1到颜色i中,观察了j条狗所用最短时间,其中包含最后一次观察的颜色

那么状态转移方程变为: \text{dp}[i][j][p]=\begin{cases} \min_{k\le\text{count}(d_i)}{\text{dp}[i-1][j-k][p]+2\times d_{i,k}}&,p=0\ \min\begin{cases} \min_{k\le\text{count}(d_i)}{\text{dp}[i-1][j-k][0]+d_{i,k}}\ \min_{k\le\text{count}(d_i)}{\text{dp}[i-1][j-k][1]+2\times d_{i,k}} \end{cases}&,p=1 \end{cases} 我们所求解为dp[C][K][1],复杂度为O(N^2)time

#include <algorithm>
#include <climits>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

int cal(const vector<int> &positions, const vector<int> &colors, int n, int k)
{
    if (k < 1)
        return 0;
    unordered_map<int, vector<int>> dogs;
    unordered_map<int, int> colorNo;
    int no = 1;
    for (int i = 0; i < n; ++i)
    {
        if (colorNo.count(colors[i]) == 0)
            colorNo[colors[i]] = no++;
    }
    for (int i = 0; i < n; ++i)
    {
        dogs[colorNo[colors[i]]].emplace_back(positions[i]);
    }
    int c = dogs.size();
    for (int i = 1; i <= c; ++i)
    {
        sort(dogs[i].begin(), dogs[i].end());
    }
    vector<vector<vector<long long>>> dp(c + 1, vector<vector<long long>>(n + 1, vector<long long>(2, INT_MAX)));
    for (int i = 1; i <= c; ++i)
    {
        dp[i][0][0] = 0;
        dp[i][0][1] = 0;
    }
    for (int i = 1; i <= dogs[1].size(); ++i)
    {
        dp[1][i][0] = 2 * dogs[1][i - 1];
        dp[1][i][1] = dogs[1][i - 1];
    }
    if (c == 1)
        return dp[c][k][1];
    for (int i = 2; i <= c; ++i)
    {
        for (int j = 1; j <= k; ++j)
        {
            for (int m = 0; m <= j && m <= dogs[i].size(); ++m)
            {
                dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j - m][0] + (m == 0 ? 0 : dogs[i][m - 1] * 2));
                dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - m][1] + (m == 0 ? 0 : dogs[i][m - 1] * 2));
                dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - m][0] + (m == 0 ? 0 : dogs[i][m - 1]));
            }
        }
    }
    return dp[c][k][1];
}

int main()
{
    int t = 0;
    cin >> t;
    for (int i = 1; i <= t; ++i)
    {
        int n, k;
        cin >> n >> k;
        vector<int> positions(n, 0), colors(n, 0);
        for (int j = 0; j < n; ++j)
        {
            cin >> positions[j];
        }
        for (int j = 0; j < n; ++j)
        {
            cin >> colors[j];
        }
        cout << "Case #" << i << ": " << cal(positions, colors, n, k) << endl;
    }
    return 0;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注