在给定约束的最小移动中将数组减少到 0

Posted

技术标签:

【中文标题】在给定约束的最小移动中将数组减少到 0【英文标题】:Reduce the array to 0 in minimum moves with given constraints 【发布时间】:2020-10-17 09:22:25 【问题描述】:

给定一个位于 x 轴上的城市。它有 n 个建筑物。第一座建筑位于 x = 1 处,高度为 h1,第二座建筑位于 x = 2 处,高度为 h2,依此类推。你是一个拿着剑想要摧毁城市的巨大怪物。你本质上也是一名计算机科学家,所以你的效率是关键,因此你想用最少的动作摧毁这座城市。

你可以做两个动作之一:

1.从 x = L 到 x = R 进行水平切割,将建筑物的高度从 x = L 到 X = R 减少 1。

2。在 x = P 处进行垂直切割,在 x = P 处完全摧毁建筑物,从而使其高度为零。**

注意:对于第一种类型的移动,从 L 到 R 范围内的每个城市必须至少有 1 个高度剩余,即您不能穿过空白区域。

打印摧毁城市所需的最少移动次数。

输入

第一行包含测试用例的数量。 对于每个测试用例,第一行包含建筑物的数量 n。 第二行包含 n 个整数,表示建筑物的高度

输出

对于每个测试用例,在新行上打印摧毁城市的最少移动次数。

注意事项

1 ≤ n ≤ 1000 0 ≤ hi ≤ 1000

示例输入 0

2

5

2 2 2 3 3

5

10 2 10 2 10

样本输出 0

3

5

我无法弄清楚这个问题的方法。 我的代码不适用于以下输入: 1 1 1 2 4 5 7 7 8 9** 在我的代码中,我减少了所有元素的最小值。然后找出零之间的子数组,然后将子数组(j-i)的长度与最小值进行比较。如果长度更小,那么我们需要跟随移动 2,否则移动 1。 我的代码:

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



                


            
        
    

【问题讨论】:

这与 java 或 python 有什么关系? :) 嘿!请发布您的代码。 我已经发布了我的代码。 您提到您的代码不适用于此输入。输出是什么,预期输出是什么? 我的输出是 9。预期的输出是 8.. 【参考方案1】:

这里的一个可能提示是,将建筑物减少到零会分隔部分,这意味着分而治之。

f(A, l, r) 表示在[l, r] 处索引的A 部分的最佳移动数。那么:

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

除了我们不需要尝试所有ks,只需要一个指向max(A)。不删除 max(A) 意味着我们要么需要执行 max(A) 移动,要么我们必须稍后删除它。

javascript 代码:

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

【讨论】:

请注意,OP 要求的是Java,而不是Javascript(虽然在编辑之后)。【参考方案2】:

您遇到的问题不在代码中,而在算法中。如果段的大小足够小,则实际上您必须执行移动 2。但是,此条件并非必不可少。

在实践中,一个简单的递归方法可以解决这个问题。在给定的段 [k, l] 中,减去最小值后,您只需执行:

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

在下文中,一个函数检测零点的位置并将对应于每个段的移动求和 并且为每个内部没有零的段调用另一个函数。

以下代码是用 C++ 编写的,但相当简单,应该很容易翻译成另一种语言。

输出:

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

提供此代码是为了完整性。重要的是算法本身。

#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";
    

【讨论】:

以上是关于在给定约束的最小移动中将数组减少到 0的主要内容,如果未能解决你的问题,请参考以下文章

在pytorch的神经网络中将参数约束为-1、0或1

在python中将所有零移动到数组的末尾

当我在故事板中将顶部约束设置为 0 时,如何避免 20 边距?

在 UITableView 标头中将标签约束设置为 0 时自动布局失败

在约束布局中拖放视图将视图移动到左上角

如何在objective-c中将约束添加到UImageView