受感染的鱼可以吃掉比自己小的鱼。所需的最少操作次数

Infected Fish can eat another fish having size less that its own. Minimum number of operation required

一位邪恶的科学家开发了一种注射剂,可以让鱼产生无法满足的饥饿感。在进行这种注射时,一条体型为 x 的鱼可以吃掉另一体型为 y (y < x) 的鱼,并成为体型为 x + y 的鱼,并保持这种饥饿感。水族馆里有许多大小不一的鱼。这位科学家将一条注射过的鱼放入这个水族箱,其中 objective 最后只剩下 1 条鱼。为了实现这一点,科学家只能进行两种类型的移动:添加任意大小的普通鱼或从水族箱中移除现有的普通鱼。给定水族箱中其他鱼的大小和注射鱼的大小,编写一个程序来确定科学家实现其 objective 所需的最少移动次数。 例如,假设水族箱中有 5 条鱼,注入的鱼的尺寸为 10,其他鱼的尺寸为 9、20、25 和 100。为确保水族箱中只剩下 1 条鱼,科学家需要移除大小为 100 的鱼并添加一条大小为 3 的鱼。因此输出为 2。步骤顺序如下所示。每一步水族箱中鱼的大小都显示在花括号中。突出显示的数字是注射鱼的大小。

输入格式: {infectedFish} # {a,b,c,d ... } 其中 a,b,c 等是正常鱼

示例:

我用下面的代码解决了这个问题。

 public static int count(int inf, int a[], int i, int op){

    //base case
    if(i==a.length){
      return op;
    }

    while(i<a.length && inf>a[i]){
      inf+=a[i];
      i++;
    }

    if(i==a.length){
      return op;
    }

    int curr = inf+inf-1;
    return Math.min(count(curr, a, i, op+1), count(inf, a, i+1, op+1));

  }

调用它使用,System.out.println(count(x, a, 0, 0)); x 是被感染的鱼,a 是给定的排序数组;

这种做法正确吗?如果是这样,它似乎有 comman 子问题,我们如何进行记忆?

让我们总结一下:

有两种可能的干预措施:
移除最大的鱼,然后 添加一条比被感染的鱼小一点的鱼。

被感染的鱼可以吞噬所有较小的鱼,每次增长的大小都是被吃掉的鱼的大小。

你希望最少的干预次数只留下吞噬者。


一个更简单的算法是:

  1. 按大小对所有正常鱼进行排序。
  2. 简单的解决方案是手动移除所有鱼。
  3. 您还没有添加任何新鱼。
  4. 尽可能多地吞噬最小的鱼。
  5. 新的候选解决方案是添加的鱼加上剩余的鱼。选择一个更好的。
  6. 如果没有鱼,你就完成了。
  7. 根据需要经常添加一条 size-1 的鱼来吞噬最小的鱼,从而将吞噬者增加到 2*size-1。
  8. 继续第 4 步,吞食鱼。

您可以通过稍微修改您的算法来提高效率。

如果你要吃的鱼不止一条,最好不要去掉下一条,然后尝试吃更大的鱼。

当你吃鱼时,你变大了,没有任何代价。

因此,备选方案是:

  • 长大后吃下一条鱼,即添加新鱼后
  • 摆脱所有剩余的鱼

那么,公式可以简化如下,其他代码不变:

return Math.min(count(curr, a, i, op+1), op + a.length -1 - i);

在这种情况下,您不需要记忆:count 函数仅遵循一种方式,始终增加其内部值。

注意:我假设这里的鱼已经排序,如您的代码中所述。

复杂度,不考虑排序:O(n + log(weight_max/weigth_in))

编辑:还有另一种方法可以加快这个过程:

如果在给定时间,操作次数op大于当前最佳值,则可以停止递归。

正如其他人回答的那样,贪心可以在这里工作。在第一次吃掉所有较小的鱼后,每个选择点是(1)添加比当前饥饿鱼小的最大可能鱼,或(2)移除所有相等或更大的鱼。显然,从中间取出一条鱼是不必要的,因为如果我们可以吃掉更大的一条,我们也可以吃掉那条。

我们要优化的状态是num_current_moves + num_removals_needed。但是因为我们只需要一个变量来存储最好看到的状态,我们可以在每次选择后更新它,迭代排序列表,贪婪地添加鱼。选项 (2) 不是主动更改;相反,它只是计算每个点的最佳可见状态的一部分。

迭代JavaScript代码:

function f(x, A){
  A.sort((a, b) => a - b)
  
  let sum = x
  let removals_needed = A.length
  let moves = 0
  let best = removals_needed
  let i = 0
  
  while (i < A.length){
    if (sum > A[i]){
      sum = sum + A[i]
      removals_needed = removals_needed - 1
      i = i + 1

    // It might not be worth
    // trying to grow the fish,
    // but the growth rate is 
    // probably fast enough not
    // to add significant time
    } else {
      sum = sum + (sum - 1)
      moves = moves + 1
    }
    
    best = Math.min(best, moves + removals_needed)
  }
  
  return best
}

var inputs = [
  [10, [9,20,25,100]], // 2
  [3, [25,20,100,400,500]], // 5
  [3, [20,21,22,23,24,25]], // 4
  [3, [20,21,22]], // 3
  [50, [25,20,9,100]] // 0
]

for (let [x, A] of inputs){
  console.log(`${x} -> ${A}`)
  console.log(f(x, A))
  console.log("")
}

public static int HungryFish(int infectedFishSize, int[] fishs)
    {
        Array.Sort(fishs);
        int moves = 0;

        for (int i = 0; i < fishs.Length; i++)
        {
            if (fishs[i] < infectedFishSize)
            {
                infectedFishSize += fishs[i];
            }
            else
            {
                if (fishs[i] < (infectedFishSize + infectedFishSize - 1))
                {
                    infectedFishSize += (infectedFishSize - 1);
                    moves++;
                }
                else
                {
                    moves += (fishs.Length - i);
                    break;
                }
            }
        }

        return moves;
    }