在条形图之间添加水

时间:2017-04-13 17:58:17

标签: algorithm

最近在类似glassdoor的网站上遇到了一个面试问题,我无法找到解决此问题的优化解决方案:

这与陷阱水问题完全不同。请仔细阅读示例。

给定一个输入数组,其中每个元素代表塔的高度,浇注的水量和索引号表示浇注水的位置。每个塔的宽度为1.浇注水后打印图形。

备注:

  1. 使用*表示塔,w代表1个水量。

  2. 浇注位置永远不会处于最高位置。不需要考虑分水箱。

    (如果你为这种情况提供了一个解决方案的补充点,你可以假设如果在高峰位置倾倒N水,N / 2水向左移动,N / 2水向右移动。)

    < / LI>

    峰值的定义:峰值位置的高度大于旁边的左右两个索引。)

    1. 假设柱状图附近有2个极高的墙壁    因此,如果水量超过直方图的容量,
         你应该指出容量数字并继续。见例2.

    2. 假设水首先离开,参见示例1

    3. 示例1:

      int[] heights = {4,2,1,2,3,2,1,0,4,2,1}
      It look like:
      
      *       *
      *   *   **
      ** ***  **
      ******* ***
      +++++++++++  <- there'll always be a base layer
      42123210431
      Assume given this heights array, water amout 3, position 2:
      

      打印:

      *       *
      *ww *   **
      **w***  **
      ******* ***
      +++++++++++ 
      

      示例2:

      int[] heights = {4,2,1,2,3,2,1,0,4,2,1}, water amout 32, position 2
      

      打印:

      capacity:21
      
      wwwwwwwwwww
      *wwwwwww*ww
      *www*www**w
      **w***ww**w
      *******w***
      +++++++++++ 
      

      起初我虽然它像the trapping water problem但我错了。有没有人有算法来解决这个问题?

      欢迎代码中的解释或评论。

      注意:

      要求捕集水问题的容量,但这个问题引入了两个变量:水量和倾倒指数。此外,水有流动的偏好。所以它不像陷阱水问题。

7 个答案:

答案 0 :(得分:1)

由于你必须生成并打印出数组,我可能会选择一种保持O(rows*columns)复杂度的递归方法。注意每个单元格可以被访问&#34;最多两次。

在高级别:首先递减,然后向左,然后向右,然后填充当前单元格。

但是,这会遇到一个小问题:(假设这是一个问题)

*w    *                *     *
**ww* *   instead of   **ww*w*

这可以通过更新算法来更正左边和右边的第一个 当前行,然后左右两个再次来填充当前行。让我们说state = v意味着我们来自上方,state = h1意味着它是第一个水平传球,state = h2意味着它是第二个水平传球。< / p>

您可以通过使用堆栈避免重复访问单元格,但它更复杂。

<强>的伪代码:

array[][] // populated with towers, as shown in the question
visited[][] // starts with all false
// call at the position you're inserting water (at the very top)
define fill(x, y, state):
   if x or y out of bounds
          or array[x][y] == '*'
          or waterCount == 0
      return

   visited = true

   // we came from above
   if state == v
      fill(x, y+1, v) // down
      fill(x-1, y, h1) // left , 1st pass
      fill(x+1, y, h1) // right, 1st pass
      fill(x-1, y, h2) // left , 2nd pass
      fill(x+1, y, h2) // right, 2nd pass

   // this is a 1st horizontal pass
   if state == h1
      fill(x, y+1, v) // down
      fill(x-1, y, h1) // left , 1st pass
      fill(x+1, y, h1) // right, 1st pass
      visited = false // need to revisit cell later
      return // skip filling the current cell

   // this is a 2nd horizontal pass
   if state == h2
      fill(x-1, y, h2) // left , 2nd pass
      fill(x+1, y, h2) // right, 2nd pass

   // fill current cell
   if waterCount > 0
      array[x][y] = 'w'
      waterCount--

答案 1 :(得分:1)

你有一个数组height,每列都有地形的高度,所以我会创建一个这个数组的副本(我们称之为水w)来表示水的高度在每一栏中。像这样你也可以摆脱不知道在转换成网格时要初始化多少行的问题,你可以完全跳过这一步。

Java代码中的算法如下所示:

public int[] getWaterHeight(int index, int drops, int[] heights) {
    int[] w = Arrays.copyOf(heights);
    for (; drops > 0; drops--) {
        int idx = index;
        // go left first
        while (idx > 0 && w[idx - 1] <= w[idx])
            idx--;
        // go right
        for (;;) {
            int t = idx + 1;
            while (t < w.length && w[t] == w[idx])
                t++;
            if (t >= w.length || w[t] >= w[idx]) {
                w[idx]++;
                break;
            } else { // we can go down to the right side here
                idx = t;
            }
        }
    }
    return w;
}

即使有很多循环,复杂性也只有O(drop * columns)。如果你期望大量的下降,那么根据最高地形点O(列)计算空的空间数是明智的,那么如果下降的数量超过空闲空间,则列高度的计算变得微不足道O(1),但是设置它们仍然需要O(列)。

答案 2 :(得分:1)

我找到了这个问题的Python解决方案。但是,我不熟悉Python所以我在这里引用代码。希望有人知道Python可以提供帮助。

代码@z026

def pour_water(terrains, location, water):
print 'location', location 
print 'len terrains', len(terrains)
waters = [0] * len(terrains)
while water > 0: 
    left = location - 1
    while left >= 0:
        if terrains[left] + waters[left] > terrains[left + 1] + waters[left + 1]:
            break
        left -= 1

    if terrains[left + 1] + waters[left + 1] < terrains[location] + waters[location]:
        location_to_pour = left + 1
        print 'set by left', location_to_pour 
    else: 
        right = location + 1
        while right < len(terrains):
            if terrains[right] + waters[right] > terrains[right - 1] + waters[right - 1]:
                print 'break, right: {}, right - 1:{}'.format(right, right - 1) 
                break
            right += 1 

        if terrains[right - 1] + waters[right - 1] < terrains[location] + waters[right - 1]:
            location_to_pour = right - 1
            print 'set by right', location_to_pour
        else:
            location_to_pour = location 
            print 'set to location', location_to_pour

    waters[location_to_pour] += 1 

    print location_to_pour
    water -= 1

max_height = max(terrains)

for height in xrange(max_height, -1, -1):
    for i in xrange(len(terrains)):
        if terrains + waters < height:
            print ' ',
        elif terrains < height <= terrains + waters:
            print 'w',
        else:
            print '+',
    print ''

答案 3 :(得分:1)

您可以从下到上遍历2D网格,为每个连续单元格的水平运行创建一个节点,然后将这些节点串在一起,形成一个链表,表示单元格的填充顺序。
enter image description here
在第一行之后,您有一个水平运行,体积为1:

1(1)

在第二行中,您会发现三个运行,其中一个连接到节点1:

1(1)->2(1)    3(1)    4(1)

在第三行中,您会发现三次运行,其中一次连接运行2和3;第3次运行最接近添加水的列,因此它首先出现:

3(1)->1(1)->2(1)->5(3)    6(1)    4(1)->7(1)

在第四行中,您会发现两次运行,其中一次连接运行6和7;第6步最接近添加水的柱子,因此它首先出现:

3(1)->1(1)->2(1)->5(3)->8(4)    6(1)->4(1)->7(1)->9(3)

在第五行中,您会找到连接第8和第9行的运行;它们位于添加水的色谱柱的两侧,因此左侧的运行首先出现:

3(1)->1(1)->2(1)->5(3)->8(4)->6(1)->4(1)->7(1)->9(3)->A(8)

运行A组合所有列,因此它成为最后一个节点,并被赋予无限的音量;任何多余的水滴都会被叠加:

3(1)->1(1)->2(1)->5(3)->8(4)->6(1)->4(1)->7(1)->9(3)->A(infinite)

然后我们按照它们列出的顺序填充运行,直到我们用完了滴。 enter image description here

答案 4 :(得分:1)

这是我的20分钟解决方案。每一滴都告诉客户端它将保留在哪里,因此完成了艰巨的任务。( IDE中的复制粘贴)现在只需要进行打印,但是滴点正在占据他们的位置。看看:

class Test2{
  private static int[] heights = {3,4,4,4,3,2,1,0,4,2,1};
  public static void main(String args[]){
  int wAmount = 10;
  int position = 2;
  for(int i=0; i<wAmount; i++){
    System.out.println(i+"#drop");
    aDropLeft(position);
  }
}

private static void aDropLeft(int position){
  getHight(position);
  int canFallTo = getFallPositionLeft(position);
  if(canFallTo==-1){canFallTo = getFallPositionRight(position);}
  if(canFallTo==-1){
    stayThere(position);
    return;
  }
  aDropLeft(canFallTo);
}

private static void stayThere(int position) {
  System.out.print("Staying at: ");log(position);
  heights[position]++;
}

//the position or -1 if it cant fall
private static int getFallPositionLeft(int position) {
  int tempHeight = getHight(position);
  int tempPosition = position;
  //check left , if no, then check right
  while(tempPosition>0){
    if(tempHeight>getHight(tempPosition-1)){
      return tempPosition-1;
    }else tempPosition--;
  }
  return -1;
}

private static int getFallPositionRight(int position) {
  int tempHeight = getHight(position);
  int tempPosition = position;
  while(tempPosition<heights.length-1){
    if(tempHeight>getHight(tempPosition+1)){
      return tempPosition+1;
    }else if(tempHeight<getHight(tempPosition+1)){
      return -1;
    }else tempPosition++;
  }
  return -1;
}   
private static int getHight(int position) {
return heights[position];
}


private static void log(int position) {
System.out.println("I am at position: " + position + " height: " + getHight(position));
}
}

当然,代码可以进行优化,但那是我直截了当的解决方案

答案 5 :(得分:0)

l=[0,1,0,2,1,0,1,3,2,1,2,1]

def findwater(l):
    w=0
    for i in range(0,len(l)-1):
        if i==0:
            pass
        else:
            num = min(max(l[:i]),max(l[i:]))-l[i]
            if num>0:
                w+=num
    return w

答案 6 :(得分:0)

master-old