2

You are given a city which lies on the x-axis. It has n buildings. The first building lies in x = 1 and has height h1, the second building lies on x = 2 and has height h2 and so on. You are a gigantic monster with a sword who wants to destroy the city. You are also a computer scientist at heart so you efficiency is the key, hence you want to destroy the city using minimum number of moves.

You can make one of the two moves :

1. Make a horizontal cut from x = L to x = R, cutting down the heights of the buildings from x = L to X = R by 1.

2. make a vertical cut at x = P, completely destroying the building at x = P thereby making its height zero.**

Note that : for the 1st type of move, every city in the range from L to R must have at least 1 height remaining, i.e. you cannot strike through an empty space.

Print the minimum number of moves needed to destroy the city.

Input

First line contains the number of test cases. For each test case, the first line contains the number of buildings n. Second line contains n integers denoting the heights of the building

Output

For every test case, print the minimum number of moves to destroy the city on a new line.

Notes

1 ≤ n ≤ 1000 0 ≤ hi ≤ 1000

Sample Input 0

2

5

2 2 2 3 3

5

10 2 10 2 10

Sample Output 0

3

5

I cannot figure out the approach to the question. My code does not work for the following input: 1 1 1 2 4 5 7 7 8 9** In my code i reduce the min value from all elements. Then find out the subarray between zeros and then compare the length of subarray(j-i) with the minimum value. if the length is less, then then we need to follow move 2, else move 1. My code:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.Scanner;

public class Main {
    static int findmin(int arr[], int i, int j) {
        int min = Integer.MAX_VALUE;

        for (int k = i; k < j; k++) {
            if (min > arr[k]) {
                min = arr[k];
            }
        }

        return min;
    }
    static void subtractmin(int arr[], int i, int j, int min) {
        //if both the length of subarray and min are equal, we destroy separately
        if (j - i <= min) {
            for (int k = i; k < j; k++) {
                // if

                arr[k] = 0;

            }

        } else {
            //subtract all
            for (int k = i; k < j; k++)

                // if

            {
                arr[k] -= min;
            }


        }
    }

    public static void main(String[] args) {
        //int input[] = {10, 2, 10, 2, 10};// 5
        //int input[] = {2, 2, 2, 3, 3};// 5
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();

        while (t-- != 0) {
            int zeros = 0;
            int n = sc.nextInt();
            int input[] = new int[n];
            int min = Integer.MAX_VALUE;

            for (int i = 0; i < n; i++) {
                input[i] = sc.nextInt();

                if (min > input[i]) {
                    min = input[i];

                }

                if (input[i] == 0) {
                    zeros++;
                }
            }

            //subtract minimum element from array
            int count = 0;

            if (zeros == 0) {
                count += min;
                subtractmin(input, 0, n, min);

            } else {
                count += min;
                subtractmin(input, 0, n, min);

            }

            //traverse the array and findsubarrays between 0's
            //1) if an element is surrounded by 0's it will be destroyed at once separately
            // 2) also if length of subarray<min element, they all need to be destroyed separately
            // 3) if min<length of subarray they need to be destroyed at once with count+=min
            int i = 0, j = 0;

            while (true) {
                //move i to the first non zero element

                for ( i = 0; i < n; i++) {
                    if (input[i] != 0) {

                        break;
                    }
                }

                //means whole array is 0;
                if (i == n) {
                    System.out.println(Math.min(count, n - zeros));

                    break;
                }

                ///start with the first non zero element and fin
                for (j = i; j <= n; j++) {
                    if ( j == n || input[j] == 0) {
                        // take out min element
                        int minEle = findmin(input, i, j)  ;
                        //if min lement is greater than subarray size, destroy separately
                        count += Math.min(minEle, j - i);

                        //System.out.println("count="+count+"min element="+minEle);
                        // subtract minimum element
                        subtractmin(input, i, j, minEle);

                    }

                    //if last elemnt is not zero



                }


            }
        }
    }
}
Emma
  • 27,428
  • 11
  • 44
  • 69
  • How is this java- or python-related? :) – michalwa Jun 26 '20 at 18:03
  • 1
    Hey! Please post your code. – akuzminykh Jun 26 '20 at 18:05
  • I have posted my code. – Raman Singh Jun 26 '20 at 18:16
  • You mentioned your code doesn't for this input. What is the output and what is the expected output? – Damien Jun 26 '20 at 18:31
  • My output is 9. The Expected output is 8.. – Raman Singh Jun 26 '20 at 18:38
  • Your algorithm should provide 9 if I understand it well. How do you expect to get a result of 8 ? – Damien Jun 26 '20 at 19:02
  • count =1 , for subtracting 1 from all elements.remaining=[0,0,0,1,3,4,6,6,7,8] , now if we look at the remaining non zero elements( 7 elements), they can be made zero by going to each one of them( by move 2) instead of move 1. Thus (j-i) logic will not produce 8, but 9 instead. – Raman Singh Jun 26 '20 at 19:24
  • So your algorithm must be modified. A simple recursive approach should do it. For a given segment of size n, no zero inside, then `n_moves = min (n, mode 1 applied)`. Difficult to go further for me, don't know java – Damien Jun 26 '20 at 19:35

2 Answers2

0

A possible hint here is that reducing a building to zero separates sections, which implies divide and conquer.

Let f(A, l, r) represent the optimal number of moves for the section of A indexed at [l, r]. Then:

f(A, l, r):
  min(
    # Reduce the whole section
    # without separating it, using
    # move 1, the horizontal cuts.
    max(A[l..r]),
    
    # Divide and conquer
    1 + f(A, l, k-1) + f(A, k+1, r)
  )
  for all l ≤ k ≤ r

Except we don't need to try all ks, just one that points to max(A). Not removing max(A) implies we would need to either perform max(A) moves or we would have to remove it later.

JavaScript code:

function findMax(A, l, r){
  let idx = l;
  for (let i=l; i<=r; i++)
    if (A[i] > A[idx])
      idx = i;
  return idx;
}

function f(A, l=0, r=A.length-1, memo={}){
  if (l > r)
    return 0;
  if (l == r)
    return 1;
    
  const key = String([l, r]);
  if (memo.hasOwnProperty(key))
    return memo[key];

  const k = findMax(A, l, r);
  const best = Math.min(A[k], 1 + f(A, l, k-1, memo) + f(A, k+1, r, memo));

  return memo[key] = best;
}

var As = [
  [2, 2, 2, 3, 3],
  [10, 2, 10, 2, 10],
  [1, 1, 1, 2, 4, 5, 7, 7, 8, 9]
];

for (let A of As)
  console.log(f(A));
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
0

The probleme you have is not in the code, but in the algorithm. If the size of a segment is small enough, effectivelly you have to perform move 2. However, this condition is not indispensable.

In practice, a simple recursive approach can solve this problem. In a given segment [k, l], after having substracted the min value, you simply have to perform:

n_moves  = min (n, vmin + min_moves(x, k, l));

In the following, one function detects positions of the zeros and sum the moves corresponding to each segment and another function is called for each segment with no zero inside.

The following code is in C++, but it is rather simple and should be easily translated to another language.

Output:

1 2 7  : 3
2 2 2 3 3  : 3
10 2 10 2 10  : 5
1 1 1 2 4 5 7 7 8 9  : 8

This code is provided for completeness. What is important is the algorithm itself.

#include    <iostream>
#include    <vector>
#include    <algorithm>

std::vector<int> get_zeros (const std::vector<int> &x, int k, int l) {
    std::vector<int> zeros;
    for (int i = k; i <= l; ++i) {
        if (x[i] == 0) zeros.push_back(i);
    }
    return zeros;
}

int min_moves (std::vector<int> &x, int k, int l);

//  This function is called after detection the position of the zeros -> no zero inside
int min_moves_no_zero (std::vector<int> &x, int k, int l) {
    int n = l-k+1;
    if (n == 0) return 0;
    if (n == 1) return 1;
    int vmin = 10000;
    for (int i = k; i <= l; ++i) {
        if (x[i] < vmin) vmin = x[i];
    }
    for (int i = k; i <= l; ++i) {
        x[i] -= vmin;
    }
    
    int nm = std::min (n, vmin + min_moves(x, k, l));
    
    return nm;
}
    
//  This function detects positions of the zeros and sum the moves corresponding to each segment
int min_moves (std::vector<int> &x, int k, int l) {
    auto zeros = get_zeros (x, k, l);
    if (zeros.size() == 0) return min_moves_no_zero (x, k, l);
    int start = k;
    int total = 0;
    for (int z = 0; z < zeros.size(); ++z) {
        int end = zeros[z] - 1;
        if (start != zeros[z]) {
            total += min_moves_no_zero (x, start, end);
        }
        start = end + 2;
    }
    if (start <= l) {
        total += min_moves_no_zero (x, start, l);
    }

    return total;
}

void print (const std::vector<int> &x) {
    for (auto k: x) {
        std::cout << k << " ";
    }
}

int main() {
    std::vector<std::vector<int>> input {
        {1, 2, 7}, 
        {2, 2, 2, 3, 3},
        {10, 2, 10, 2, 10},
        {1, 1, 1, 2, 4, 5, 7, 7, 8, 9}
    };
    
    for (auto& arr: input) {
        auto save = arr;
        int moves = min_moves (arr, 0, arr.size()-1);
        print (save);
        std::cout << " : " << moves << "\n";
    }
}
Damien
  • 4,809
  • 4
  • 15
  • 20