Google Kickstart Round A 2021-Rabbit House

Posted

技术标签:

【中文标题】Google Kickstart Round A 2021-Rabbit House【英文标题】: 【发布时间】:2021-03-21 07:18:29 【问题描述】:

谁能告诉我我的代码有什么问题?我已经通过了示例测试,但我无法通过两个实际的测试用例..

我的算法是dfs:

    从一个矩阵的第一个元素到最后,检查一个元素与相邻的上、下、左、右的差值是否小于等于1

    如果是,则通过 如果不是,则将最小数量的块添加到检查元素或相邻元素中以产生差异 1 并将计数增加您添加的最小块数

    如何将最小数量添加到哪个元素: 只需选择产生绝对差异所需的最小元素 1

    例如,您将 aij 与较低的 ai+1j 进行了比较: 如果您添加到检查元素是因为它更小,则再次从 aij+1、aij-1、ai-1j 开始递归。因为现在 aij 和 ai+1j 之间的差异符合您需要检查的标准是否与其他相邻的 否则从 ai+1j 开始递归

    对下、上、左、右执行上述步骤

6.返回计数

问题出在: https://codingcompetitions.withgoogle.com/kickstart/round/0000000000436140/000000000068cb14

问题 芭芭拉去年在学校取得了很好的成绩,所以她的父母决定送她一只宠物兔子。她非常兴奋,为兔子建造了一个房子,可以看作是一个 R 行 C 列的 2D 网格。

兔子喜欢跳跃,所以芭芭拉在网格的几个单元格上堆放了几个盒子。每个盒子都是一个具有相同尺寸的立方体,它们与网格中一个单元格的尺寸完全匹配。

但是,芭芭拉很快意识到,兔子跳的高度超过 1 格可能很危险,因此她决定通过对房子进行一些调整来避免这种情况。对于每一对相邻的单元格,芭芭拉希望它们的绝对高度差最多为 1 个盒子。如果两个单元格共享一个公共边,则认为它们是相邻的。

由于所有盒子都是强力胶粘的,Barbara 无法移除最初存在的任何盒子,但她可以在它们上面添加盒子。她可以添加任意数量的框,添加任意数量的单元格(可能为零)。帮助他确定要添加的最少盒子总数是多少,这样兔子的房子就安全了。

输入 输入的第一行给出了测试用例的数量,T.T 测试用例紧随其后。

每个测试用例都以包含两个整数 R 和 C 的行开头。

然后,接下来是 R 行,每行都有 C 个整数。第 i 行的第 j 个整数 Gi,j 表示位于网格第 i 行第 j 列的单元格上最初有多少个框。

输出 对于每个测试用例,输出一行包含 Case #x: y,其中 x 是测试用例编号(从 1 开始),y 是要添加的最小盒子数,以便兔子的房子是安全的。

限制 内存限制:1 GB。 1≤T≤100。 0≤Gi,j≤2⋅106,对于所有 i, j。 测试集 1 时间限制:20秒。 1≤R,C≤50。 测试集 2 时间限制:40秒。 1≤R,C≤300。

这是我的 C++ 代码:

#include <iostream>
#include <string>
#include <cstdlib>
#include <vector>

using namespace std;

void solve(vector<vector<int>> &v,
    int ii, int jj,
    int r, int c, int& cnt) 
    if (ii<0 || ii>r - 1 || jj<0 || jj>c - 1) return;
    int ads;
    if (ii + 1 < r) 
        if (abs(v[ii][jj] - v[ii + 1][jj]) > 1) 
            ads = min(abs(v[ii + 1][jj] - v[ii][jj] - 1),
                abs(v[ii + 1][jj] - v[ii][jj] + 1));
                cnt += ads;
            if (v[ii + 1][jj] < v[ii][jj]) 
                v[ii + 1][jj] += ads;
                 solve(v, ii + 1, jj, r, c, cnt);
            
            else 
                v[ii][jj] += ads;
                solve(v, ii, jj - 1, r, c, cnt);
                solve(v, ii, jj + 1, r, c, cnt);
                solve(v, ii - 1, jj, r, c, cnt);
            
        
    
    if (ii - 1 > -1) 
        if (abs(v[ii][jj] - v[ii - 1][jj]) > 1) 
            ads = min(abs(v[ii - 1][jj] - v[ii][jj] - 1),
                abs(v[ii - 1][jj] - v[ii][jj] + 1));
                cnt += ads;
            if (v[ii - 1][jj] < v[ii][jj]) 
                v[ii - 1][jj] += ads;
                solve(v, ii - 1, jj, r, c, cnt);
            
            else 
                v[ii][jj] += ads;
                solve(v, ii, jj - 1, r, c, cnt);
                solve(v, ii, jj + 1, r, c, cnt);
                solve(v, ii + 1, jj, r, c, cnt);
            
        
    
    if (jj - 1 > -1) 
        if (abs(v[ii][jj] - v[ii][jj - 1]) > 1) 
            ads = min(abs(v[ii][jj - 1] - v[ii][jj] - 1),
                abs(v[ii][jj - 1] - v[ii][jj] + 1));
                cnt += ads;
            if (v[ii][jj - 1] < v[ii][jj]) 
                v[ii][jj - 1] += ads;
                solve(v, ii, jj - 1, r, c, cnt);
            
            else 
                v[ii][jj] += ads;
                solve(v, ii, jj + 1, r, c, cnt);
                solve(v, ii - 1, jj, r, c, cnt);
                solve(v, ii + 1, jj, r, c, cnt);
            
        
    
    if (jj + 1 < c) 
        if (abs(v[ii][jj] - v[ii][jj + 1]) > 1) 
            ads = min(abs(v[ii][jj + 1] - v[ii][jj] - 1),
                abs(v[ii][jj + 1] - v[ii][jj] + 1));
                cnt += ads;
            if (v[ii][jj + 1] < v[ii][jj]) 
                v[ii][jj + 1] += ads;
                solve(v, ii, jj + 1, r, c, cnt);
            
            else 
                v[ii][jj] += ads;
                solve(v, ii, jj - 1, r, c, cnt);
                solve(v, ii - 1, jj, r, c, cnt);
                solve(v, ii + 1, jj, r, c, cnt);
            
        
    
    return;


int main() 
        // your code goes here
        int t;
        cin>>t;
        int r, c, m;
        for (int i = 0; i < t; i++) 
            cin >> r >> c;
            vector<vector<int>> v(r,vector<int>(c,0));
            for (int j=0;j<r;j++)
                for (int k=0;k<c;k++)
                    cin>>m;
                    v[j][k]=m;
                
            
            int cnt=0;
            for (int j=0;j<r;j++)
                for (int k=0;k<c;k++)
                    solve(v,j,k,r,c,cnt);
                
            
            cout << "Case #" << i + 1 << ": " << cnt<<endl;
        
        return 0;

我已经通过了示例案例: 样本 样本输入 3 1 3 3 4 3 1 3 3 0 0 3 3 0 0 0 0 2 0 0 0 0 样本输出 案例#1:0 案例#2:3 案例#3:4

但不是实际的测试用例...非常感谢任何帮助!

【问题讨论】:

提示:你不需要abs 函数,你需要一个优先队列。顺便说一句,如果你描述你的算法,你可能会有更多的运气,而不是期望人们对代码进行逆向工程来发现你的算法。 谢谢,我不明白为什么我需要优先级队列,但我按照你的建议描述了我的算法。谢谢! 啊,我现在明白你要做什么了。似乎该算法可以工作,并且代码似乎通常遵循该算法。我没有在代码中看到任何特别错误的地方,但是有很多机会出现一对一错误和 x-y 交换。要调试这样的代码,您需要测试代码中所有可能路径的示例输入。 我将描述我暗示的方法,以防您感兴趣。找到矩阵中最高的堆栈(称为高度 H)。调整四个相邻的堆栈,使它们至少有 H-1 个盒子高。冲洗并重复。这种方法的问题在于它是 O(N^2),其中 N=R*C。解决方案是使用优先级队列,以便在每个步骤中轻松找到最高的堆栈。使用优先级队列,运行时间减少到 O(NlogN)。 我已经测试了许多样本并调试了代码,但看不出它为什么会失败。我在这个问题上花了很多时间,所以我希望有人能看到我错过的错误????但是感谢您的评论 nlogn 比我的快! 【参考方案1】:

正如在 cmets 中已经指出的那样,解决方案是从最高的盒子开始,并在需要时设置其邻居的高度值。

简单的实现是 O(n^2)。

按照 cmets 中的建议,我尝试使用最大堆。然而,这个解决方案不够快(TLE),因为一旦更新了矩阵的高度值,就必须重建最大堆。我可能会改进这个实现。我更喜欢另一种方式。

最后,我通过使用带有专用比较器的std::multiset 获得了足够快的解决方案。

当一个单元格的高度被更新时,它会被移除并重新插入到多重集中,每个操作都是 O(logn)。

全局复杂度:O(n logn),其中n 是矩阵的元素数 `n = R * C*。

#include <iostream>
#include <string>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <set>
#include <iterator>

struct Coord 
    int x;
    int y;
    friend std::ostream& operator<<(std::ostream& os, const Coord& c) 
        os << "(" << c.x << ", " << c.y << ")";
        return os;
    
;

long long int solution (std::vector<std::vector<int>>& heights, int R, int C) 
    std::vector<Coord> depl = -1, 0, 1, 0, 0, -1, 0, 1;
    long long int count = 0;
    auto Comp = [&heights] (const Coord& c0, const Coord& c1) 
        if (heights[c0.x][c0.y] == heights[c1.x][c1.y]) 
            if (c0.x == c1.x) return (c0.y < c1.y);
            return (c0.x < c1.x);
        
        return heights[c0.x][c0.y] > heights[c1.x][c1.y];
    ;
    
    std::multiset<Coord, decltype(Comp)> Cells (Comp);
    for (int i = 0; i < R; ++i) 
        for (int j = 0; j < C; ++j) 
            Cells.insert (i, j);
        
    

    auto isValid = [&] (int i, int j) 
        return (i >= 0) && (i < R) && (j >= 0) && (j < C);
    ;
    
    while (Cells.size() > 1) 
        auto n = Cells.size();
        auto top = Cells.begin();
        auto pivot = *top;
        int h_ref = heights[pivot.x][pivot.y];
        Cells.erase (top);
        
        for (auto& d: depl) 
            int x = pivot.x + d.x;
            int y = pivot.y + d.y;
            if (!isValid(x, y)) continue;
            int h = heights[x][y];
            if (h <= h_ref - 2) 
                count += h_ref - h - 1;
                Cells.erase(x, y);
                heights[x][y] = h_ref - 1;
                Cells.insert(x, y);
            
        
    
    return count;


int main() 
    std::ios_base::sync_with_stdio(false); 
    std::cin.tie(NULL); 
    std::cout.tie(0);
    int nt;
    std::cin >> nt;
    for (int t = 1; t <= nt; t++) 
        int R, C;
        std::cin >> R >> C;
        std::vector<std::vector<int>> v(R, std::vector<int>(C));
        for (int j = 0; j < R; j++) 
            for (int k = 0; k < C; k++) 
                int m;
                std::cin >> m;
                v[j][k] = m;
            
        
        auto cnt = solution (v, R, C);
        std::cout << "Case #" << t << ": " << cnt << std::endl;
    
    return 0;

【讨论】:

感谢您的回答! 欢迎您。有趣的问题。如果对你有帮助,请采纳答案吗? 谢谢。但我更好奇为什么我的代码会失败,就像我在 OP 中写的那样:)

以上是关于Google Kickstart Round A 2021-Rabbit House的主要内容,如果未能解决你的问题,请参考以下文章

Google Kickstart 2020 Round A 分配问题

2019 Google Kickstart Round H

Google Kickstart Round A 2021-Rabbit House

kickstart 2021 round A

Kickstart2018 Round (Practice ~ H)

kickstart_Round C 2020