严格递增的整数序列的计数

问题描述

给定两个长度为 n 的整数 a 和 b 的列表,求严格递增的整数序列的个数 I[0]

a = [6,3,4,4]
b = [1,5,1,6]
Four possible solutions are possible
[1 3 4 5],[1 3 4 6],[2 3 4 5],[2 3 4 6].
The length of both arrays will not exceed 100. 
Each element is between 1 and 10000.```

Can someone provide a hint for this question? I am not able to figure out

解决方法

您可以在此处使用动态编程方法。您的 DP 的状态将是当前索引和最后一个值,如 count_sequence(int index,int last_value) ,循环 min(a[index],b[index])max(a[index],b[index]) ,如果有一个大于然后 last_value 使用当前值转到下一个索引像 count_sequence(index+1,current_value),如果您达到基本情况,即 index>=n,这意味着您找到了一个序列,然后返回 1。使用它,您可以找到所有可能的序列。

通过记忆,复杂度为 O(n*m),
n = 数组大小m = 最大元素

,

JavaScript/node.js 解决方案:

    const a = [6,3,4,4]
    const b = [1,5,1,6]
    const n = a.length

    let min = []
    let max = []

    for (let i = n - 1; i >= 0; i--) {
      let itemMin = (min[i] = Math.min(a[i],b[i]))
      let itemMax = (max[i] = Math.max(a[i],b[i]))

      if (i > 0) {
        const maxBefore = Math.max(a[i - 1],b[i - 1])

        // max of before index,>= current min
        if (maxBefore >= min[i]) {
          itemMin = Math.min(maxBefore + 1,max[i])
        }
      }

      if (i < n - 1) {
        const minAfter = min[i + 1]

        // min of after index,<= current or calculated min
        if (itemMin >= minAfter) {
          itemMin = Math.min(minAfter - 1,min[i])
        }

        // max correction
        itemMax = Math.min(max[i],minAfter - 1)
      }

      // Test is solution possible,if not do something
      if (itemMin < min[i] || itemMin > max[i]) {
        throw new Error('Impossible to solve...')
      }
      if (itemMax < min[i] || itemMax > max[i]) {
        throw new Error('Impossible to solve...')
      }

      // Set min and max values
      min[i] = itemMin
      max[i] = itemMax
    }

    // ***** Printing results
    function printResult(min,max,str = '',item = 0) {
      const n = min.length

      for (let j = min[item]; j <= max[item]; j++) {
        if (item < n - 1) {
          printResult(min,`${item !== 0 ? str : ''}${j},`,item + 1)
        } else {
          console.log(`[${str}${j}]`)
        }
      }
    }

    printResult(min,max)

,

给定两个数组,一个保存 min 值,另一个保存每个索引的 max 值,您可以定义一个简单的递归函数来导出每个可能的递增序列,每个数组的起始值都在位置是当前 min 值和之前选择的值加 1 的最大值。

下面是一些 Java 代码来说明:

static void incSeq(int[] min,int[] max,int[] res,int pos,int prev)
{
    if(pos == min.length)
    {
        System.out.println("seq: " + Arrays.toString(res));
        return;
    }
    
    for(int j = Math.max(prev+1,min[pos]); j <= max[pos]; j++)
    {
        res[pos] = j;
        incSeq(min,res,pos+1,j);
    }
}

但是,如果我们考虑如下输入:

min = {1,1}
max = {100,100,5}

我们可以看到,我们的函数将做很多无用的工作,遍历前三个位置的值,这些位置不会是递增序列的一部分。

我们可以通过认识到我们需要考虑的位置 i 的最大值比位置 i+1 处的最大值小一来解决这个问题。如果我们从最后一个位置开始并使用此规则向后工作,我们可以将上面的 max 数组更新为:

max = {2,5}

这意味着我们的递归函数的工作量会减少很多。

完整代码如下:

public static void main(String[] args)
{
    int n = 4;
    int[] a = {6,4};
    int[] b = {1,6};

    int[] min = new int[n];
    int[] max = new int[n];
    
    System.out.println("a:   " + Arrays.toString(a));
    System.out.println("b:   " + Arrays.toString(b));
    
    for(int i=0; i<n; i++)
    {
        min[i] = Math.min(a[i],b[i]);
        max[i] = Math.max(a[i],b[i]);
    }
    
    System.out.println("min: " + Arrays.toString(min));
    System.out.println("max: " + Arrays.toString(max));
    
    // cap max values
    for(int i=n-1; i>0; i--)
    {
        max[i-1] = Math.min(max[i-1],max[i]-1);
    }
    
    System.out.println("max: " + Arrays.toString(max) + "\n");
    
    incSeq(min,new int[n],0);
}

static void incSeq(int[] min,j);
    }
}

输出:

a:   [6,4]
b:   [1,6]
min: [1,4]
max: [6,6]
max: [2,6]

seq: [1,5]
seq: [1,6]
seq: [2,5]
seq: [2,6]