【问题标题】:Find the amount of water in ith cup in a pyramid structure?找出金字塔结构中第 i 个杯子里的水量?
【发布时间】:2012-08-01 17:37:18
【问题描述】:

这个问题是在论坛上提出的。有什么建议吗?

有一个金字塔,第 1 层有 1 个杯子,第 2 层有 2 个,第 3 层有 3 个,依此类推.. 它看起来像这样

  1
 2 3
4 5 6

每个杯子的容量为 C。你从顶部倒 L 升水。当杯子 1 装满时,它会均匀地溢出到杯子 2,3,当它们装满时,杯子 4 和 6 只从 2 和 3 分别得到水,但 5 会从两个杯子中取水,依此类推。 现在给定 C 和 L。找出第 i 杯中的水量?

【问题讨论】:

  • 我解决这个问题的初步步骤: 1.如果给定一个杯子索引i(示例4)如何找到这个杯子位于哪个级别? 2. 找出第 i 个杯子的水源数 2. 找出每级注满后还剩下多少输入水?
  • 您最初尝试的方法不起作用吗?
  • 你看过流量网络吗? en.wikipedia.org/wiki/Flow_network
  • 好像有Paskal's triangle的链接
  • 这可能属于math.stackexchange.com

标签: algorithm data-structures graph


【解决方案1】:

每个玻璃杯都有一个流入,玻璃中有一个一定量的水,也许还有一些流出(溢出)。 p>

如果每个玻璃杯可以装 1 个单位的水,而你倒了 15 个单位的水,你会得到以下结果(括号中的溢出量):

Incoming flow = 15, capacity = 1

Level 1:               1(14)
Level 2:           1(6)     1(6)
Level 3:       1(2)     1(5)     1(2)
Level 4:    1(1)   1(2.5)  1(2.5)    1(1)
Level 5:   1  1(0.75)  1(1.5)  1(0.75)   1
Level 6:  0 0.375 1(0.125) 1(0.125) 0.375 0
Level 7: 0 0  0.0625   0.125    0.0625   0 0

第一层的流入是L。来自玻璃cr流入Fin(c, r),可以写成:

Fin(0, r) = 0
Fin(r+1, r) = 0
Fin(1, 1) = L
Fin(c, r) = Fout(c - 1, r - 1)/2 + Fout(c, r - 1)/2

那个玻璃杯里的水量是:

A(c, r) = Min(C, Fin(c, r))

流出的流量是:

Fout(c, r) = Max(0, Fin(c, r) - C)

我没有看到任何明显的公式来评估 A(c, r) 而不进行递归。


要从索引到行和玻璃位置,您可以这样做:

index = r*(r-1)/2 + c

r = floor((1 + sqrt(8*index - 7))/2)
c = index - r*(r-1)/2

(indexes start with 1)

【讨论】:

  • 你是怎么想出r的公式的?基本上我们有一个公式:index = r*(r-1)/2 +c 有 2 个未知数:r 和 c。你如何从那里得到 r?
  • @Maggie 如果您将c 设置为 1,您将获得每行第一个玻璃杯的索引。 (index = r(r-1)/2+1) 如果你为r 解决这个问题,你会得到给定任何索引的行的公式。虽然如果它不是该行的第一个玻璃杯,您将有一个不需要的小数部分。所以你用floor()包装它。
  • 谢谢,我现在明白了。您的算法需要稍作修改,否则:很好的解决方案和很好的解释!
  • 您好,您能更详细地解释一下您是如何得出索引公式的吗? '索引 = r*(r-1)/2 + 2'? 'r*(r-1)/2' 的逻辑是什么?
  • @JaviDorfsman 第一行r 上的眼镜总数就是所谓的Triangular numbers。 (1, 3, 6, 10, ...)。要获得任何玻璃的索引,您需要计算该行 上方 (r*(r-1)/2) 的玻璃数量,并将玻璃数量添加到该行的左侧玻璃 (c-1),加上玻璃本身 (1)。
【解决方案2】:

如果将金字塔建模为图形,则问题会转化为广度优先搜索。当您遍历每个节点时,获取其邻居并存储它们的溢出量。如果一个邻居已经被前一个节点检索到(这将在 5 个节点的情况下发生,因为节点 2 和节点 3 有一条边),您将必须根据其容量和已填充的内容更新溢出(通过节点 2;假设节点 2 在节点 3 之前被遍历)。

【讨论】:

    【解决方案3】:

    一些想法: (1) 重要的是知道哪两个杯子是第 i 个杯子的输入。 (2) 重要的是要知道从左侧给你带来水的最小 Lleft 以及从右侧给你带来水的 Lright 水平 (3) 所以你需要知道哪些杯子为杯子提供水。如果你从 0 开始编号,这更容易,思考得更快。第 i 个杯子将填充 (i-1)*2+1 和 i*2,这意味着第 k 个杯子将从 (for k%2=1) ( k-1)/2 和 (k+1)/2 (对于 k%2=0) k/2 和 k/2+1 (4) 这样,您应该检查对于任何 L,您将计算 L-Lleft 和 L-Lright 的差值。当为正时,提供的水是计算出的差除以 2^n 的结果,其中 n 是杯子的高度。

    【讨论】:

      【解决方案4】:

      计算二项式系数的帕斯卡三角解可以用来解决这个问题。我们只需要稍微调整算法,而不是计算二项式系数,而是计算水位。给定第 i 个杯子,我们计算 level 和 index 以找出杯子在三角形中的位置。

      杯子被建模为

          0         Level 1
        1   2       Level 2
      3   4   5     Level 3
      

      getIndex() 和 getLevel() 返回索引和级别。索引和级别从 1 开始。

      public static int getIndex(int i) {
          int totalNodes = i + 1;
          double d = (-3 + Math.sqrt(9 - 8*(1-totalNodes)))/2;
          int level = (int)Math.floor(d);
          int total = ((level+1)*(level+2))/2;
          int index = 0;
          if(total==totalNodes) index = level;
          else{
              level++;
              index = totalNodes - total - 1;
          }
      
          return ++index;
      }
      
      public static int getLevel(int i) {
          int totalNodes = i + 1;
          double d = (-3 + Math.sqrt(9 - 8*(1-totalNodes)))/2;
          int level = (int)Math.floor(d);
          int total = ((level+1)*(level+2))/2;
          int index = 0;
          if(total==totalNodes) index = level;
          else{
              level++;
              index = totalNodes - total - 1;
          }
      
          return ++level;
      }
      

      k 是从 0 开始的第 k 个杯子。C 是杯子容量,L 是总水量。

      public static double getWaterLevel(double C, double L, int k) {
          int n = getLevel(k);
          int index = getIndex(k);
          double[] water = new double[index+1];
      
          water[1] = L;
      
          for(int i = 2; i <= n; i++)
          {
              boolean overflowed = false;
      
              for(int j = Math.min(i, index); j > 0; j--) {
                  double over = 0;
                  if(water[j]>C) over = (water[j]-C)/2;
                  if(water[j-1]>C) over += (water[j-1]-C)/2;
      
                  water[j] = over;
      
                  if(!overflowed && over!=0) overflowed=true;
              }
      
              if(!overflowed) break; // no more overflow. stop 
          }
      
          return water[index] > C ? C : water[index];
      }
      

      【讨论】:

        【解决方案5】:

        这是另一个简单的解决方案,只需将水倒入当前的玻璃杯中,然后检查是否有多余的水流到下一个水平。在这里,我使用了 2D Mat 来倒水。然后我将 2D 垫子转换为 1D 尺寸等于我们需要返回并返回的第 i 个元素/玻璃。实施明智,这是一个非常简单的解决方案。

        private double fillWaterInGlasses(double capacity, double water , int glassToFind) {
            int maxLevel = (int)(water/capacity)/2 + 1;
            double[][] glasses = new double[maxLevel][maxLevel];
            // Pour total water in top glass initially.
            glasses[0][0] = water;
            int level=0;
            boolean waterInLevel = true;
            while(waterInLevel) {
                waterInLevel = false;
                // For each glass in the level.
                for(int j=0; j<=level; j++) {
                    // If the glass has more liquid then it can store then pour it to glasses under it.
                    if(glasses[level][j] > capacity) {
                        double extraWater = glasses[level][j] - capacity;
                        glasses[level][j] = capacity;
                        glasses[level+1][j] += extraWater / 2;
                        glasses[level+1][j+1] += extraWater / 2;
                        waterInLevel = true;
                    }
                }
                level++;
            }
            double res[] = new double[glassToFind];
            int k =0;
            for (int i = 0; i < glasses.length; i++) {
                for (int j = 0; j <= i; j++) {
                    res[k] = glasses[i][j];
                    if (k == glassToFind-1){
                        return res[glassToFind-1];
                    }
                    k++;
                }
            }
            return res[glassToFind-1];
        }
        

        【讨论】:

          【解决方案6】:

          这是一个简单易懂的实现:

          public class main {
              static float total_water = 50;
              static int N = 20;
              static glass[][] pyramid = new glass[N][N];
          
              public static void main(String[] args) {
                  build_pyramid();
                  pour_water(0, 0, total_water);
                  print_pyramid();
                  print_total_water_stored();
              }
          
              private static void print_total_water_stored() {
                  float total = 0;
                  for (int i = 0; i < N; i++) {
                      for (int j = 0; j <= i; j++)
                          total += pyramid[i][j].filled;
                  }
                  System.out.println("total water stored= " + total);
              }
          
              private static void pour_water(int row, int col, float water) {
                  if (water >= (pyramid[row][col].capacity - pyramid[row][col].filled)) {
                      water -= (pyramid[row][col].capacity - pyramid[row][col].filled);
                      pyramid[row][col].filled = pyramid[row][col].capacity;
                      pour_water(row + 1, col, water / 2);
                      pour_water(row + 1, col + 1, water / 2);
                  } else {
                      pyramid[row][col].filled += water;
                  }
              }
          
              public static void build_pyramid() {
                  for (int i = 0; i < N; i++) {
                      for (int j = 0; j < N; j++)
                          pyramid[i][j] = new glass(1);
                  }
              }
          
              public static void print_pyramid() {
                  for (int i = 0; i < N; i++) {
                      for (int j = 0; j <= i; j++)
                          System.out.print(pyramid[i][j].filled + " ");
                      System.out.println();
                  }
              }
          }
          
          class glass {
              float capacity;
              float filled;
          
              glass(float cap) {
                  capacity = cap;
                  filled = 0;
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-10-26
            • 1970-01-01
            • 1970-01-01
            • 2012-09-18
            • 1970-01-01
            • 2016-09-18
            相关资源
            最近更新 更多