问题描述
给定两个长度为 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]