【问题标题】:Dynamic Programming: Number of moves to reduce the array to 1 number动态规划:将数组减少到 1 个数的移动次数
【发布时间】:2019-06-22 11:06:09
【问题描述】:

我正在尝试使用动态编程表填充方法来解决优化问题,但我无法在最优子结构中分解问题。

这就是问题所在:

给定一个排序数组,从种子元素开始,你可以将数组减少到只有一个元素。

  1. 您可以删除除种子之外的数组元素,这被视为 1 步,这会减少数组。
  2. 您可以将小于种子的数组元素添加到种子。这被视为 1 步。
  3. 您可以使用以前的方法向种子中添加任意次数。
  4. 种子可以“消耗”比它少的任何元素。这会减少数组。

例如,这是排序后的数组

3, 20, 50, 100, 400

第一个元素是种子。

第一个元素还不能消耗下一个更大的元素,所以我们需要添加它。根据约束,我们可以在 1 步中添加任何小于种子的内容。所以,假设我们加了 2。

moves = 1
seed = seed + 2
5, 20, 50, 100, 400

还是种子不能消费下一个元素,所以我们加4

moves = 2
seed = seed + 4
9, 20, 50, 100, 400

moves = 3
seed = seed + 8
17, 20, 50, 100, 400

moves = 4
seed = seed + 16
33, 20, 50, 100, 400

现在种子可以“消耗”下一个元素 所以减少的数组变成了

53 (33 + 20) , 50, 100 , 400

53可以消耗50变成103

103, 100, 400

103可以化100变成203

203, 400

203是种子,不能消耗400 所以

moves = 5
seed = seed + 202
405, 400

现在可以消费变成805了

我们也可以简单地删除 400,这将被视为 1 步。

总移动次数 = 5

但还有另一种解决方案,即从数组中删除所有元素(20、50、100、400),这需要 4 次移动。

所以在这种情况下,最小移动数是 4。

尝试从动态规划的角度来考虑它,我可以看到在每一步我们都有两个选择,是消耗元素还是删除元素。似乎要考虑的路径总数是所有 2^n 路径。

但我未能将其分解为重叠的子问题或最优子结构,甚至无法定义递归关系。

动态编程是正确的方法吗?

一些观察:

  1. 当我们必须向种子添加一些东西时,最好的方法似乎是添加尽可能高的允许值,即种子 - 1
  2. 一旦我们决定删除一个元素,这意味着后面的元素也将被删除。为什么,因为如果我们决定多次向种子添加对当前元素无效,那么对于下一个更大的元素也是如此。

【问题讨论】:

  • 数组的最大大小可以是多少,每个数字可以有多大?
  • 你可以使用 (max{given array}+1)* array_size 时间和内存复杂度来解决。
  • 我是否理解正确,消费不计入移动?
  • @גלעדברקן 消耗不被认为是一个动作,所以只要有比种子小的元素,它们在 0 个动作中被消耗
  • @user58697 是的,这是正确的

标签: arrays algorithm dynamic-programming


【解决方案1】:

您可以通过使用 (max{given array}+1)* array_size 时间和内存复杂度来解决它。

int dp[array_size][max{given array}+1]={INF} // initially all index holds infinity
int seed=array[seed_index];
int Max=max{given array}+1;
dp[0][seed]=0; 
for(int i=1;i<=n;i++){  // Consuming or removing ith element
   for(int j=1;j<=Max;j++){ // if the current seed value is j
       //Consider the removing case
       dp[i][j]=min(dp[i][j],dp[i-1][j]+1);
       // Increasing seed value
        dp[i-1][min(Max,j+(j-1))]=min(dp[i-1][min(Max,j+(j-1))],dp[i-1][j]+1);
       // Consider the consuming case
       if(j>array[i]){
          dp[i][min(Max,j+array[i])]=min(dp[i-1][j],dp[i][min(Max,j+array[i])]);  // If j is the current seed value and you want to consume ith element then  after consuming the ith element the seed will be j+array[i] but if it is greater than Max we can take upto max . Because for consuming the maximum value we need only the Max value. 
       }
   }
}

// Now check the smallest possible value for the latest number.
result = INF;
for(int i=0;i<=Max;i++)result=min(result,dp[n][i]);

这里 dp[i][j] 表示减少到数组的第 i 个值的结果,在减少到第 i 个值之后,你的种子将是 j 。并且 dp[i][j] 将保存移动次数,以减少到第 i 个值到 1 个具有种子值 j 的元素。

在消费情况下:
如果 j 是当前种子值并且您想消耗第 i 个元素,那么在消耗第 i 个元素后,种子将是 j+array[i] 但如果它大于 Max 我们可以使用 Max 。因为要消耗最大值,我们只需要 max{given array} 的最大值+1。

递归方法:

int dp[array_size][max{given array}+1]={-1} // initially all index holds -1
int Max=max{givenarray}+1;

int DP(int seed,int index){
   if(index==n+1)return 0;
   if(dp[seed][index]!=-1)return dp[seed][index];// previously calculated
   int res=inf;
   // By removing
    res=DP(seed,i+1)+1;
   //Increasing seed
   if(seed<Max)
      res=min(res,1+DP(min(Max,seed+seed-1),index));
   // By consuming
   if(seed>array[i])
      res=min(res,DP(seed+array[i],i+1));
   dp[seed][index]=res;
  return res;
}

【讨论】:

  • 你能解释一下你的消费情况吗?
  • 添加了一些解释。
  • 你能解释一下你是如何处理它的吗?或者这段代码如何处理附加成本?我知道消费情况只有在种子大于数组值时才有效,但是 dp 中的每个元素代表什么?
  • 抱歉上次忘记增加种子值了。
  • 我也添加了递归方法。请看一下!
【解决方案2】:

我不认为这是一个 DP 问题。

您可以通过删除最小元素a(以及其他所有内容;您的第二个观察结果是正确的)或使用它来摆脱它。

移除将采取n 移动,消耗将采取log (a/s) 移动(移动有效地使种子翻倍)。取其少;如果您选择增加种子和消耗,请将相同的逻辑应用于下一个元素。

【讨论】:

    【解决方案3】:

    请注意,目标消费之间的删除没有意义,因为给定一个种子 s 和下一个元素; abc;为了能够消费bc 接下来,我们必须首先增加s 超过a 然后超过b。但是,如果我们使用“增加”移动将s 增加到那么远,它只能在消耗c 的过程中节省我们消耗ab 的移动(并且永远不会添加移动)。 (我想你得出了类似的结论。)

    所以我们的决定,从左到右遍历,始终是是否停止增加种子并从这一点到最后删除所有内容。正如已经提到的,我们可以通过观察给定的种子s 和下一个元素a 来计算O(1) 的任何需要增加需要多少移动:

    (s - 1) * 2^m + 1 > a
    2^m > (a - 1) / (s - 1)
    m > log2((a - 1) / (s - 1))
    

    那么总遍历将是 O(n)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-23
      • 2012-09-05
      • 2012-10-17
      • 2019-04-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多