【问题标题】:Waterfall Rock Hit Counter瀑布岩石命中计数器
【发布时间】:2018-05-11 08:51:36
【问题描述】:

我在解决课堂上的一个问题时遇到了困难,它是关于动态编程的(或者我的教授这样称呼它)。这个问题被称为瀑布岩石撞击。 (时间限制:1s,内存限制:16mb)

给定岩石的左上角 (v1, h1) 和右下角坐标 (v2, h2),我们模拟瀑布并计算被水击中的岩石数量,想象水从某个坐标开始落下(x,y),它会下落到(x-1, y)并继续下落,直到撞到一块石头。当它碰到一块岩石时,水会左右分裂,并跟随岩石的长度,这是算法如何工作的图片。 Simulation Picture.

这里我们需要注意的是,如果岩石被多次击中,问题也保证不会有任何岩石相互粘连,并且水总是会通过任何 2 块岩石找到出路。

这是我的一段不完整的代码,我仍在考虑第二个条件,即水撞击岩石并防止重复计数。

int maks=0, n, m, nstone;
struct a{
   int v1, v2, h1, h2; //coordinates
   bool pass; //passed or not?
}; a arr[5000];

bool acompare(a lhs, a rhs){
   return  lhs.v1 < rhs.v1; //compare height descending
}

int fall(int x, int y){
   if (x == n || y == m || y == -1) //if the water passed the wall
      return 0;
   else if () //the confusing condition if the water hit the rock
      return 1 + fall(x, h1-1) + fall(x, h2+1));
   else // if there's nothing below
      return fall(x-1, y);
}

int main(){
   cin>> n>> m>> nstone; //waterfall size (n*m) and number of stone
   for (int i=0; i<nstone; i++){ //read the stone's corner
      cin>> arr[i].v1>> arr[i].h1>> arr[i].v2>> arr[i].h2; 
      arr[i].pass = false;
   }

   sort(arr, arr+nstone, acompare); //sort the stone's based on height
   cin>> start; //read the start point of the water
   cout<< fall(start, m)<< endl;

   return 0;
}

测试用例样本输入:

6 6 3  
2 3 2 4  
4 2 5 2  
5 5 6 5  

输出:

3  

【问题讨论】:

  • 看不出来这个DP怎么样,这只是递归。
  • @user202729 是的,我能理解你的意思,但是当我和我的教授谈起这个问题时,他坚持认为这个问题是一个 DP,但我会记住并在这个问题中添加递归标签
  • 有时间限制吗?你肯定不想要太低效的代码吧?
  • 并添加c++
  • 那么……我觉得让你自己做运行时分析会更好。

标签: c++ c++11 recursion dynamic-programming


【解决方案1】:

首先,您遇到问题的条件。在每一步迭代所有岩石效率低下,即使二分搜索也不是最优的。这可以通过一张桌子来解决,就像在棋盘游戏中一样。为此创建一个 NxM 2D 数组(例如,每个项目 4 字节 = 250000 * 4 字节 = 2MB,远离您的 16MB 限制)并标记所有被岩石覆盖的点。在每个步骤中,您都需要检查一个点是否是岩石。

其次,这是一个 DP 问题,因为流可以像底部中间的那样合并:

..|..
.|X|.
.|.|.
|X|X|
|.|.|

您的方法会计算此流两次。 (顺便说一句,我看不到您在哪里使用您的bool pass; //passed or not? 成员)一旦计算存储流在您的棋盘游戏桌上击中的岩石数量,例如在你的例子中:

.1331.
.1XX1.
010.1.
0X0010
0X00X0
0.00X0

因此,下次您到达已计算的溪流点时,您已经知道从该点击中了多少石头。

【讨论】:

    【解决方案2】:

    我看到您不使用 std::vector 而是使用 C 样式数组。我假设您不熟悉 C++ STL 库,因此将提供尽可能少使用 C++ STL 的答案。


    因为要检查多个石头,所以需要使用循环(或std::find_if)来检查所有石头。

    for(int stone_index=0;stone_index<nstone;++stone_index)
    

    对于每块石头,检查(x,y) 坐标是否正好在该石头的顶部。

    一旦你找到了,保证你不会再找到其他的(因为石头不碰),所以立即返回。

    return 1 + fall(x, h1-1) + fall(x, h2+1));
    

    (这只是伪代码,将h1h2替换为合适的值)

    如果你在没有找到石头的情况下到达循环的尽头,

    // if there's nothing below
    return fall(x-1, y);
    

    请注意,此解决方案需要nstone 步骤来检查,您可以使用std::map 或其他东西做得更好。这对你来说可能太难了。

    【讨论】:

    • 我理解这个想法,老实说,这是我解决这个问题的第一个想法,但它可以在 1s 内完成吗?
    猜你喜欢
    • 1970-01-01
    • 2015-08-17
    • 1970-01-01
    • 2020-12-11
    • 1970-01-01
    • 2011-10-11
    • 1970-01-01
    • 2020-01-14
    • 1970-01-01
    相关资源
    最近更新 更多