简体   繁体   中英

Dynamic Programming: Number of moves to reduce the array to 1 number

I ma trying to approach an optimization problem with dynamic programming table filling approach, but I haven't been able to break down the problem in optimal substructures.

This is the problem:

Given a sorted array, starting with the seed element, in how many moves can you reduce the array to have only one element.

  1. You can remove an array element except for the seed, which is considered as 1 move, this reduces the array.
  2. you can add an array element which is less than the seed, to the seed. This is considered 1 move.
  3. You can add to the seed using previous approach, as many times as you want.
  4. The seed can "consume" any element less than it. This reduces the array.

for example, this is the sorted array

3, 20, 50, 100, 400

The first element is the seed.

The first element can't consume the next bigger element yet, so we need to add to it. According to the constraints we can add anything less than the seed, in 1 move. So, let's say we add 2.

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

still seed can't consume the next element, so let's add 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

now the seed can "consume" the next element so reduced array becomes

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

53 can consume 50 and become 103

103, 100, 400

103 can cosume 100 and become 203

203, 400

203 is the seed, it can't consume 400 so

moves = 5
seed = seed + 202
405, 400

now it can consume and become 805

we could have simply removed 400 as well, which would be counted as 1 move.

total moves = 5

But there is another solution, that is, to remove all the elements (20, 50, 100, 400) from the array which would take 4 moves.

So in this case, minimum number of moves is 4.

Trying to think of it in terms of dynamic programming, i can see that at every step we have 2 choices, whether to consume the element, or to remove the element. It also seems that the total number of paths to consider is all the 2^n paths.

but I am failing to break it into overlapping subproblems or optimal substructure, or even defining a recurrence relation.

Is dynamic programming the right approach here?

Some observations:

  1. when we have to add something to the seed, the best approach seems to be to add the highest possible allowed value which is seed - 1
  2. once we decide to remove an element, it means that the elements following that would be removed as well. Why, because if we have made a decision that adding multiple times to the seed is futile for the current element, it will hold true for the next bigger element as well.

You can solve it by using (max{given array}+1)* array_size time and memory complexity.

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]);

Here dp[i][j] represents the result of reducing up to ith value of the array and after reducing up to ith value thee seed will be j . And dp[i][j] will holds the number of moves to reduce up to ith value to 1 element with seed value j.

In consume case :
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 maximum+1 of the max{given array} .

Recursive Approach:

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;
}

I don't see it as a DP problem.

You can get rid of the smallest element a by either removing it (and everything else; your second observation is correct) or consuming it.

Removal will take n moves, consuming will take log (a/s) moves (the move effectively doubles the seed). Do whichever is less; if you chose to increase the seed and consume, apply the same logic to the next element.

Observe that removals between target consumptions make no sense because given a seed, s , and next elements; a , b and c ; to be able to consume b or c next, we necessarily have to increase s first past a and then past b . But if we've increased s that far utilising "increase" moves, it could only save us moves (and never add moves) to consume a and b on the way to consuming c . (I think you came to a similar conclusion.)

So our decision, traversing from left to right, is always whether to quit increasing the seed and remove everything from this point to the end. As has been alluded to, we can calculate how many moves are needed for any one needed increase in O(1) by observing that given seed, s and next element, a :

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

Total traversal then would be O(n).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM