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 分配问题
Google Kickstart Round A 2021-Rabbit House